162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Probe module for 8250/16550-type Exar chips PCI serial ports. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Based on drivers/tty/serial/8250/8250_pci.c, 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2017 Sudip Mukherjee, All Rights Reserved. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/acpi.h> 1062306a36Sopenharmony_ci#include <linux/dmi.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/pci.h> 1562306a36Sopenharmony_ci#include <linux/property.h> 1662306a36Sopenharmony_ci#include <linux/serial_core.h> 1762306a36Sopenharmony_ci#include <linux/serial_reg.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/string.h> 2062306a36Sopenharmony_ci#include <linux/tty.h> 2162306a36Sopenharmony_ci#include <linux/8250_pci.h> 2262306a36Sopenharmony_ci#include <linux/delay.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <asm/byteorder.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "8250.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_COM_2S 0x1052 2962306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_COM_4S 0x105d 3062306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_COM_8S 0x106c 3162306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_COM232_8 0x10a8 3262306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_COM_2SM 0x10d2 3362306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_COM_4SM 0x10db 3462306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_COM_8SM 0x10ea 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define PCI_DEVICE_ID_COMMTECH_4224PCI335 0x0002 3762306a36Sopenharmony_ci#define PCI_DEVICE_ID_COMMTECH_4222PCI335 0x0004 3862306a36Sopenharmony_ci#define PCI_DEVICE_ID_COMMTECH_2324PCI335 0x000a 3962306a36Sopenharmony_ci#define PCI_DEVICE_ID_COMMTECH_2328PCI335 0x000b 4062306a36Sopenharmony_ci#define PCI_DEVICE_ID_COMMTECH_4224PCIE 0x0020 4162306a36Sopenharmony_ci#define PCI_DEVICE_ID_COMMTECH_4228PCIE 0x0021 4262306a36Sopenharmony_ci#define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0022 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define PCI_DEVICE_ID_EXAR_XR17V4358 0x4358 4562306a36Sopenharmony_ci#define PCI_DEVICE_ID_EXAR_XR17V8358 0x8358 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define PCI_SUBDEVICE_ID_USR_2980 0x0128 4862306a36Sopenharmony_ci#define PCI_SUBDEVICE_ID_USR_2981 0x0129 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define PCI_DEVICE_ID_SEALEVEL_710xC 0x1001 5162306a36Sopenharmony_ci#define PCI_DEVICE_ID_SEALEVEL_720xC 0x1002 5262306a36Sopenharmony_ci#define PCI_DEVICE_ID_SEALEVEL_740xC 0x1004 5362306a36Sopenharmony_ci#define PCI_DEVICE_ID_SEALEVEL_780xC 0x1008 5462306a36Sopenharmony_ci#define PCI_DEVICE_ID_SEALEVEL_716xC 0x1010 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define UART_EXAR_INT0 0x80 5762306a36Sopenharmony_ci#define UART_EXAR_8XMODE 0x88 /* 8X sampling rate select */ 5862306a36Sopenharmony_ci#define UART_EXAR_SLEEP 0x8b /* Sleep mode */ 5962306a36Sopenharmony_ci#define UART_EXAR_DVID 0x8d /* Device identification */ 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define UART_EXAR_FCTR 0x08 /* Feature Control Register */ 6262306a36Sopenharmony_ci#define UART_FCTR_EXAR_IRDA 0x10 /* IrDa data encode select */ 6362306a36Sopenharmony_ci#define UART_FCTR_EXAR_485 0x20 /* Auto 485 half duplex dir ctl */ 6462306a36Sopenharmony_ci#define UART_FCTR_EXAR_TRGA 0x00 /* FIFO trigger table A */ 6562306a36Sopenharmony_ci#define UART_FCTR_EXAR_TRGB 0x60 /* FIFO trigger table B */ 6662306a36Sopenharmony_ci#define UART_FCTR_EXAR_TRGC 0x80 /* FIFO trigger table C */ 6762306a36Sopenharmony_ci#define UART_FCTR_EXAR_TRGD 0xc0 /* FIFO trigger table D programmable */ 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define UART_EXAR_TXTRG 0x0a /* Tx FIFO trigger level write-only */ 7062306a36Sopenharmony_ci#define UART_EXAR_RXTRG 0x0b /* Rx FIFO trigger level write-only */ 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define UART_EXAR_MPIOINT_7_0 0x8f /* MPIOINT[7:0] */ 7362306a36Sopenharmony_ci#define UART_EXAR_MPIOLVL_7_0 0x90 /* MPIOLVL[7:0] */ 7462306a36Sopenharmony_ci#define UART_EXAR_MPIO3T_7_0 0x91 /* MPIO3T[7:0] */ 7562306a36Sopenharmony_ci#define UART_EXAR_MPIOINV_7_0 0x92 /* MPIOINV[7:0] */ 7662306a36Sopenharmony_ci#define UART_EXAR_MPIOSEL_7_0 0x93 /* MPIOSEL[7:0] */ 7762306a36Sopenharmony_ci#define UART_EXAR_MPIOOD_7_0 0x94 /* MPIOOD[7:0] */ 7862306a36Sopenharmony_ci#define UART_EXAR_MPIOINT_15_8 0x95 /* MPIOINT[15:8] */ 7962306a36Sopenharmony_ci#define UART_EXAR_MPIOLVL_15_8 0x96 /* MPIOLVL[15:8] */ 8062306a36Sopenharmony_ci#define UART_EXAR_MPIO3T_15_8 0x97 /* MPIO3T[15:8] */ 8162306a36Sopenharmony_ci#define UART_EXAR_MPIOINV_15_8 0x98 /* MPIOINV[15:8] */ 8262306a36Sopenharmony_ci#define UART_EXAR_MPIOSEL_15_8 0x99 /* MPIOSEL[15:8] */ 8362306a36Sopenharmony_ci#define UART_EXAR_MPIOOD_15_8 0x9a /* MPIOOD[15:8] */ 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#define UART_EXAR_RS485_DLY(x) ((x) << 4) 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* 8862306a36Sopenharmony_ci * IOT2040 MPIO wiring semantics: 8962306a36Sopenharmony_ci * 9062306a36Sopenharmony_ci * MPIO Port Function 9162306a36Sopenharmony_ci * ---- ---- -------- 9262306a36Sopenharmony_ci * 0 2 Mode bit 0 9362306a36Sopenharmony_ci * 1 2 Mode bit 1 9462306a36Sopenharmony_ci * 2 2 Terminate bus 9562306a36Sopenharmony_ci * 3 - <reserved> 9662306a36Sopenharmony_ci * 4 3 Mode bit 0 9762306a36Sopenharmony_ci * 5 3 Mode bit 1 9862306a36Sopenharmony_ci * 6 3 Terminate bus 9962306a36Sopenharmony_ci * 7 - <reserved> 10062306a36Sopenharmony_ci * 8 2 Enable 10162306a36Sopenharmony_ci * 9 3 Enable 10262306a36Sopenharmony_ci * 10 - Red LED 10362306a36Sopenharmony_ci * 11..15 - <unused> 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* IOT2040 MPIOs 0..7 */ 10762306a36Sopenharmony_ci#define IOT2040_UART_MODE_RS232 0x01 10862306a36Sopenharmony_ci#define IOT2040_UART_MODE_RS485 0x02 10962306a36Sopenharmony_ci#define IOT2040_UART_MODE_RS422 0x03 11062306a36Sopenharmony_ci#define IOT2040_UART_TERMINATE_BUS 0x04 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci#define IOT2040_UART1_MASK 0x0f 11362306a36Sopenharmony_ci#define IOT2040_UART2_SHIFT 4 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci#define IOT2040_UARTS_DEFAULT_MODE 0x11 /* both RS232 */ 11662306a36Sopenharmony_ci#define IOT2040_UARTS_GPIO_LO_MODE 0x88 /* reserved pins as input */ 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* IOT2040 MPIOs 8..15 */ 11962306a36Sopenharmony_ci#define IOT2040_UARTS_ENABLE 0x03 12062306a36Sopenharmony_ci#define IOT2040_UARTS_GPIO_HI_MODE 0xF8 /* enable & LED as outputs */ 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistruct exar8250; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistruct exar8250_platform { 12562306a36Sopenharmony_ci int (*rs485_config)(struct uart_port *port, struct ktermios *termios, 12662306a36Sopenharmony_ci struct serial_rs485 *rs485); 12762306a36Sopenharmony_ci const struct serial_rs485 *rs485_supported; 12862306a36Sopenharmony_ci int (*register_gpio)(struct pci_dev *, struct uart_8250_port *); 12962306a36Sopenharmony_ci void (*unregister_gpio)(struct uart_8250_port *); 13062306a36Sopenharmony_ci}; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/** 13362306a36Sopenharmony_ci * struct exar8250_board - board information 13462306a36Sopenharmony_ci * @num_ports: number of serial ports 13562306a36Sopenharmony_ci * @reg_shift: describes UART register mapping in PCI memory 13662306a36Sopenharmony_ci * @setup: quirk run at ->probe() stage 13762306a36Sopenharmony_ci * @exit: quirk run at ->remove() stage 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_cistruct exar8250_board { 14062306a36Sopenharmony_ci unsigned int num_ports; 14162306a36Sopenharmony_ci unsigned int reg_shift; 14262306a36Sopenharmony_ci int (*setup)(struct exar8250 *, struct pci_dev *, 14362306a36Sopenharmony_ci struct uart_8250_port *, int); 14462306a36Sopenharmony_ci void (*exit)(struct pci_dev *pcidev); 14562306a36Sopenharmony_ci}; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistruct exar8250 { 14862306a36Sopenharmony_ci unsigned int nr; 14962306a36Sopenharmony_ci struct exar8250_board *board; 15062306a36Sopenharmony_ci void __iomem *virt; 15162306a36Sopenharmony_ci int line[]; 15262306a36Sopenharmony_ci}; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void exar_pm(struct uart_port *port, unsigned int state, unsigned int old) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci /* 15762306a36Sopenharmony_ci * Exar UARTs have a SLEEP register that enables or disables each UART 15862306a36Sopenharmony_ci * to enter sleep mode separately. On the XR17V35x the register 15962306a36Sopenharmony_ci * is accessible to each UART at the UART_EXAR_SLEEP offset, but 16062306a36Sopenharmony_ci * the UART channel may only write to the corresponding bit. 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_ci serial_port_out(port, UART_EXAR_SLEEP, state ? 0xff : 0); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/* 16662306a36Sopenharmony_ci * XR17V35x UARTs have an extra fractional divisor register (DLD) 16762306a36Sopenharmony_ci * Calculate divisor with extra 4-bit fractional portion 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_cistatic unsigned int xr17v35x_get_divisor(struct uart_port *p, unsigned int baud, 17062306a36Sopenharmony_ci unsigned int *frac) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci unsigned int quot_16; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci quot_16 = DIV_ROUND_CLOSEST(p->uartclk, baud); 17562306a36Sopenharmony_ci *frac = quot_16 & 0x0f; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return quot_16 >> 4; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic void xr17v35x_set_divisor(struct uart_port *p, unsigned int baud, 18162306a36Sopenharmony_ci unsigned int quot, unsigned int quot_frac) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci serial8250_do_set_divisor(p, baud, quot, quot_frac); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* Preserve bits not related to baudrate; DLD[7:4]. */ 18662306a36Sopenharmony_ci quot_frac |= serial_port_in(p, 0x2) & 0xf0; 18762306a36Sopenharmony_ci serial_port_out(p, 0x2, quot_frac); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int xr17v35x_startup(struct uart_port *port) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci /* 19362306a36Sopenharmony_ci * First enable access to IER [7:5], ISR [5:4], FCR [5:4], 19462306a36Sopenharmony_ci * MCR [7:5] and MSR [7:0] 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_ci serial_port_out(port, UART_XR_EFR, UART_EFR_ECB); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* 19962306a36Sopenharmony_ci * Make sure all interrups are masked until initialization is 20062306a36Sopenharmony_ci * complete and the FIFOs are cleared 20162306a36Sopenharmony_ci * 20262306a36Sopenharmony_ci * Synchronize UART_IER access against the console. 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci spin_lock_irq(&port->lock); 20562306a36Sopenharmony_ci serial_port_out(port, UART_IER, 0); 20662306a36Sopenharmony_ci spin_unlock_irq(&port->lock); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return serial8250_do_startup(port); 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic void exar_shutdown(struct uart_port *port) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci bool tx_complete = false; 21462306a36Sopenharmony_ci struct uart_8250_port *up = up_to_u8250p(port); 21562306a36Sopenharmony_ci struct circ_buf *xmit = &port->state->xmit; 21662306a36Sopenharmony_ci int i = 0; 21762306a36Sopenharmony_ci u16 lsr; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci do { 22062306a36Sopenharmony_ci lsr = serial_in(up, UART_LSR); 22162306a36Sopenharmony_ci if (lsr & (UART_LSR_TEMT | UART_LSR_THRE)) 22262306a36Sopenharmony_ci tx_complete = true; 22362306a36Sopenharmony_ci else 22462306a36Sopenharmony_ci tx_complete = false; 22562306a36Sopenharmony_ci usleep_range(1000, 1100); 22662306a36Sopenharmony_ci } while (!uart_circ_empty(xmit) && !tx_complete && i++ < 1000); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci serial8250_do_shutdown(port); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic int default_setup(struct exar8250 *priv, struct pci_dev *pcidev, 23262306a36Sopenharmony_ci int idx, unsigned int offset, 23362306a36Sopenharmony_ci struct uart_8250_port *port) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci const struct exar8250_board *board = priv->board; 23662306a36Sopenharmony_ci unsigned int bar = 0; 23762306a36Sopenharmony_ci unsigned char status; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci port->port.iotype = UPIO_MEM; 24062306a36Sopenharmony_ci port->port.mapbase = pci_resource_start(pcidev, bar) + offset; 24162306a36Sopenharmony_ci port->port.membase = priv->virt + offset; 24262306a36Sopenharmony_ci port->port.regshift = board->reg_shift; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* 24562306a36Sopenharmony_ci * XR17V35x UARTs have an extra divisor register, DLD that gets enabled 24662306a36Sopenharmony_ci * with when DLAB is set which will cause the device to incorrectly match 24762306a36Sopenharmony_ci * and assign port type to PORT_16650. The EFR for this UART is found 24862306a36Sopenharmony_ci * at offset 0x09. Instead check the Deice ID (DVID) register 24962306a36Sopenharmony_ci * for a 2, 4 or 8 port UART. 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_ci status = readb(port->port.membase + UART_EXAR_DVID); 25262306a36Sopenharmony_ci if (status == 0x82 || status == 0x84 || status == 0x88) { 25362306a36Sopenharmony_ci port->port.type = PORT_XR17V35X; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci port->port.get_divisor = xr17v35x_get_divisor; 25662306a36Sopenharmony_ci port->port.set_divisor = xr17v35x_set_divisor; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci port->port.startup = xr17v35x_startup; 25962306a36Sopenharmony_ci } else { 26062306a36Sopenharmony_ci port->port.type = PORT_XR17D15X; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci port->port.pm = exar_pm; 26462306a36Sopenharmony_ci port->port.shutdown = exar_shutdown; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci return 0; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic int 27062306a36Sopenharmony_cipci_fastcom335_setup(struct exar8250 *priv, struct pci_dev *pcidev, 27162306a36Sopenharmony_ci struct uart_8250_port *port, int idx) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci unsigned int offset = idx * 0x200; 27462306a36Sopenharmony_ci unsigned int baud = 1843200; 27562306a36Sopenharmony_ci u8 __iomem *p; 27662306a36Sopenharmony_ci int err; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci port->port.uartclk = baud * 16; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci err = default_setup(priv, pcidev, idx, offset, port); 28162306a36Sopenharmony_ci if (err) 28262306a36Sopenharmony_ci return err; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci p = port->port.membase; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci writeb(0x00, p + UART_EXAR_8XMODE); 28762306a36Sopenharmony_ci writeb(UART_FCTR_EXAR_TRGD, p + UART_EXAR_FCTR); 28862306a36Sopenharmony_ci writeb(32, p + UART_EXAR_TXTRG); 28962306a36Sopenharmony_ci writeb(32, p + UART_EXAR_RXTRG); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* 29262306a36Sopenharmony_ci * Setup Multipurpose Input/Output pins. 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ci if (idx == 0) { 29562306a36Sopenharmony_ci switch (pcidev->device) { 29662306a36Sopenharmony_ci case PCI_DEVICE_ID_COMMTECH_4222PCI335: 29762306a36Sopenharmony_ci case PCI_DEVICE_ID_COMMTECH_4224PCI335: 29862306a36Sopenharmony_ci writeb(0x78, p + UART_EXAR_MPIOLVL_7_0); 29962306a36Sopenharmony_ci writeb(0x00, p + UART_EXAR_MPIOINV_7_0); 30062306a36Sopenharmony_ci writeb(0x00, p + UART_EXAR_MPIOSEL_7_0); 30162306a36Sopenharmony_ci break; 30262306a36Sopenharmony_ci case PCI_DEVICE_ID_COMMTECH_2324PCI335: 30362306a36Sopenharmony_ci case PCI_DEVICE_ID_COMMTECH_2328PCI335: 30462306a36Sopenharmony_ci writeb(0x00, p + UART_EXAR_MPIOLVL_7_0); 30562306a36Sopenharmony_ci writeb(0xc0, p + UART_EXAR_MPIOINV_7_0); 30662306a36Sopenharmony_ci writeb(0xc0, p + UART_EXAR_MPIOSEL_7_0); 30762306a36Sopenharmony_ci break; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci writeb(0x00, p + UART_EXAR_MPIOINT_7_0); 31062306a36Sopenharmony_ci writeb(0x00, p + UART_EXAR_MPIO3T_7_0); 31162306a36Sopenharmony_ci writeb(0x00, p + UART_EXAR_MPIOOD_7_0); 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return 0; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic int 31862306a36Sopenharmony_cipci_connect_tech_setup(struct exar8250 *priv, struct pci_dev *pcidev, 31962306a36Sopenharmony_ci struct uart_8250_port *port, int idx) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci unsigned int offset = idx * 0x200; 32262306a36Sopenharmony_ci unsigned int baud = 1843200; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci port->port.uartclk = baud * 16; 32562306a36Sopenharmony_ci return default_setup(priv, pcidev, idx, offset, port); 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic int 32962306a36Sopenharmony_cipci_xr17c154_setup(struct exar8250 *priv, struct pci_dev *pcidev, 33062306a36Sopenharmony_ci struct uart_8250_port *port, int idx) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci unsigned int offset = idx * 0x200; 33362306a36Sopenharmony_ci unsigned int baud = 921600; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci port->port.uartclk = baud * 16; 33662306a36Sopenharmony_ci return default_setup(priv, pcidev, idx, offset, port); 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic void setup_gpio(struct pci_dev *pcidev, u8 __iomem *p) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci /* 34262306a36Sopenharmony_ci * The Commtech adapters required the MPIOs to be driven low. The Exar 34362306a36Sopenharmony_ci * devices will export them as GPIOs, so we pre-configure them safely 34462306a36Sopenharmony_ci * as inputs. 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci u8 dir = 0x00; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if ((pcidev->vendor == PCI_VENDOR_ID_EXAR) && 35062306a36Sopenharmony_ci (pcidev->subsystem_vendor != PCI_VENDOR_ID_SEALEVEL)) { 35162306a36Sopenharmony_ci // Configure GPIO as inputs for Commtech adapters 35262306a36Sopenharmony_ci dir = 0xff; 35362306a36Sopenharmony_ci } else { 35462306a36Sopenharmony_ci // Configure GPIO as outputs for SeaLevel adapters 35562306a36Sopenharmony_ci dir = 0x00; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci writeb(0x00, p + UART_EXAR_MPIOINT_7_0); 35962306a36Sopenharmony_ci writeb(0x00, p + UART_EXAR_MPIOLVL_7_0); 36062306a36Sopenharmony_ci writeb(0x00, p + UART_EXAR_MPIO3T_7_0); 36162306a36Sopenharmony_ci writeb(0x00, p + UART_EXAR_MPIOINV_7_0); 36262306a36Sopenharmony_ci writeb(dir, p + UART_EXAR_MPIOSEL_7_0); 36362306a36Sopenharmony_ci writeb(0x00, p + UART_EXAR_MPIOOD_7_0); 36462306a36Sopenharmony_ci writeb(0x00, p + UART_EXAR_MPIOINT_15_8); 36562306a36Sopenharmony_ci writeb(0x00, p + UART_EXAR_MPIOLVL_15_8); 36662306a36Sopenharmony_ci writeb(0x00, p + UART_EXAR_MPIO3T_15_8); 36762306a36Sopenharmony_ci writeb(0x00, p + UART_EXAR_MPIOINV_15_8); 36862306a36Sopenharmony_ci writeb(dir, p + UART_EXAR_MPIOSEL_15_8); 36962306a36Sopenharmony_ci writeb(0x00, p + UART_EXAR_MPIOOD_15_8); 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic struct platform_device *__xr17v35x_register_gpio(struct pci_dev *pcidev, 37362306a36Sopenharmony_ci const struct software_node *node) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct platform_device *pdev; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci pdev = platform_device_alloc("gpio_exar", PLATFORM_DEVID_AUTO); 37862306a36Sopenharmony_ci if (!pdev) 37962306a36Sopenharmony_ci return NULL; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci pdev->dev.parent = &pcidev->dev; 38262306a36Sopenharmony_ci ACPI_COMPANION_SET(&pdev->dev, ACPI_COMPANION(&pcidev->dev)); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (device_add_software_node(&pdev->dev, node) < 0 || 38562306a36Sopenharmony_ci platform_device_add(pdev) < 0) { 38662306a36Sopenharmony_ci platform_device_put(pdev); 38762306a36Sopenharmony_ci return NULL; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci return pdev; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic void __xr17v35x_unregister_gpio(struct platform_device *pdev) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci device_remove_software_node(&pdev->dev); 39662306a36Sopenharmony_ci platform_device_unregister(pdev); 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic const struct property_entry exar_gpio_properties[] = { 40062306a36Sopenharmony_ci PROPERTY_ENTRY_U32("exar,first-pin", 0), 40162306a36Sopenharmony_ci PROPERTY_ENTRY_U32("ngpios", 16), 40262306a36Sopenharmony_ci { } 40362306a36Sopenharmony_ci}; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic const struct software_node exar_gpio_node = { 40662306a36Sopenharmony_ci .properties = exar_gpio_properties, 40762306a36Sopenharmony_ci}; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic int xr17v35x_register_gpio(struct pci_dev *pcidev, struct uart_8250_port *port) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci if (pcidev->vendor == PCI_VENDOR_ID_EXAR) 41262306a36Sopenharmony_ci port->port.private_data = 41362306a36Sopenharmony_ci __xr17v35x_register_gpio(pcidev, &exar_gpio_node); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci return 0; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic void xr17v35x_unregister_gpio(struct uart_8250_port *port) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci if (!port->port.private_data) 42162306a36Sopenharmony_ci return; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci __xr17v35x_unregister_gpio(port->port.private_data); 42462306a36Sopenharmony_ci port->port.private_data = NULL; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic int generic_rs485_config(struct uart_port *port, struct ktermios *termios, 42862306a36Sopenharmony_ci struct serial_rs485 *rs485) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci bool is_rs485 = !!(rs485->flags & SER_RS485_ENABLED); 43162306a36Sopenharmony_ci u8 __iomem *p = port->membase; 43262306a36Sopenharmony_ci u8 value; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci value = readb(p + UART_EXAR_FCTR); 43562306a36Sopenharmony_ci if (is_rs485) 43662306a36Sopenharmony_ci value |= UART_FCTR_EXAR_485; 43762306a36Sopenharmony_ci else 43862306a36Sopenharmony_ci value &= ~UART_FCTR_EXAR_485; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci writeb(value, p + UART_EXAR_FCTR); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (is_rs485) 44362306a36Sopenharmony_ci writeb(UART_EXAR_RS485_DLY(4), p + UART_MSR); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci return 0; 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic const struct serial_rs485 generic_rs485_supported = { 44962306a36Sopenharmony_ci .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND, 45062306a36Sopenharmony_ci}; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic const struct exar8250_platform exar8250_default_platform = { 45362306a36Sopenharmony_ci .register_gpio = xr17v35x_register_gpio, 45462306a36Sopenharmony_ci .unregister_gpio = xr17v35x_unregister_gpio, 45562306a36Sopenharmony_ci .rs485_config = generic_rs485_config, 45662306a36Sopenharmony_ci .rs485_supported = &generic_rs485_supported, 45762306a36Sopenharmony_ci}; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic int iot2040_rs485_config(struct uart_port *port, struct ktermios *termios, 46062306a36Sopenharmony_ci struct serial_rs485 *rs485) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci bool is_rs485 = !!(rs485->flags & SER_RS485_ENABLED); 46362306a36Sopenharmony_ci u8 __iomem *p = port->membase; 46462306a36Sopenharmony_ci u8 mask = IOT2040_UART1_MASK; 46562306a36Sopenharmony_ci u8 mode, value; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (is_rs485) { 46862306a36Sopenharmony_ci if (rs485->flags & SER_RS485_RX_DURING_TX) 46962306a36Sopenharmony_ci mode = IOT2040_UART_MODE_RS422; 47062306a36Sopenharmony_ci else 47162306a36Sopenharmony_ci mode = IOT2040_UART_MODE_RS485; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (rs485->flags & SER_RS485_TERMINATE_BUS) 47462306a36Sopenharmony_ci mode |= IOT2040_UART_TERMINATE_BUS; 47562306a36Sopenharmony_ci } else { 47662306a36Sopenharmony_ci mode = IOT2040_UART_MODE_RS232; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (port->line == 3) { 48062306a36Sopenharmony_ci mask <<= IOT2040_UART2_SHIFT; 48162306a36Sopenharmony_ci mode <<= IOT2040_UART2_SHIFT; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci value = readb(p + UART_EXAR_MPIOLVL_7_0); 48562306a36Sopenharmony_ci value &= ~mask; 48662306a36Sopenharmony_ci value |= mode; 48762306a36Sopenharmony_ci writeb(value, p + UART_EXAR_MPIOLVL_7_0); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci return generic_rs485_config(port, termios, rs485); 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic const struct serial_rs485 iot2040_rs485_supported = { 49362306a36Sopenharmony_ci .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | 49462306a36Sopenharmony_ci SER_RS485_RX_DURING_TX | SER_RS485_TERMINATE_BUS, 49562306a36Sopenharmony_ci}; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic const struct property_entry iot2040_gpio_properties[] = { 49862306a36Sopenharmony_ci PROPERTY_ENTRY_U32("exar,first-pin", 10), 49962306a36Sopenharmony_ci PROPERTY_ENTRY_U32("ngpios", 1), 50062306a36Sopenharmony_ci { } 50162306a36Sopenharmony_ci}; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic const struct software_node iot2040_gpio_node = { 50462306a36Sopenharmony_ci .properties = iot2040_gpio_properties, 50562306a36Sopenharmony_ci}; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic int iot2040_register_gpio(struct pci_dev *pcidev, 50862306a36Sopenharmony_ci struct uart_8250_port *port) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci u8 __iomem *p = port->port.membase; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci writeb(IOT2040_UARTS_DEFAULT_MODE, p + UART_EXAR_MPIOLVL_7_0); 51362306a36Sopenharmony_ci writeb(IOT2040_UARTS_GPIO_LO_MODE, p + UART_EXAR_MPIOSEL_7_0); 51462306a36Sopenharmony_ci writeb(IOT2040_UARTS_ENABLE, p + UART_EXAR_MPIOLVL_15_8); 51562306a36Sopenharmony_ci writeb(IOT2040_UARTS_GPIO_HI_MODE, p + UART_EXAR_MPIOSEL_15_8); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci port->port.private_data = 51862306a36Sopenharmony_ci __xr17v35x_register_gpio(pcidev, &iot2040_gpio_node); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci return 0; 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic const struct exar8250_platform iot2040_platform = { 52462306a36Sopenharmony_ci .rs485_config = iot2040_rs485_config, 52562306a36Sopenharmony_ci .rs485_supported = &iot2040_rs485_supported, 52662306a36Sopenharmony_ci .register_gpio = iot2040_register_gpio, 52762306a36Sopenharmony_ci .unregister_gpio = xr17v35x_unregister_gpio, 52862306a36Sopenharmony_ci}; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci/* 53162306a36Sopenharmony_ci * For SIMATIC IOT2000, only IOT2040 and its variants have the Exar device, 53262306a36Sopenharmony_ci * IOT2020 doesn't have. Therefore it is sufficient to match on the common 53362306a36Sopenharmony_ci * board name after the device was found. 53462306a36Sopenharmony_ci */ 53562306a36Sopenharmony_cistatic const struct dmi_system_id exar_platforms[] = { 53662306a36Sopenharmony_ci { 53762306a36Sopenharmony_ci .matches = { 53862306a36Sopenharmony_ci DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"), 53962306a36Sopenharmony_ci }, 54062306a36Sopenharmony_ci .driver_data = (void *)&iot2040_platform, 54162306a36Sopenharmony_ci }, 54262306a36Sopenharmony_ci {} 54362306a36Sopenharmony_ci}; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic const struct exar8250_platform *exar_get_platform(void) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci const struct dmi_system_id *dmi_match; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci dmi_match = dmi_first_match(exar_platforms); 55062306a36Sopenharmony_ci if (dmi_match) 55162306a36Sopenharmony_ci return dmi_match->driver_data; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci return &exar8250_default_platform; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic int 55762306a36Sopenharmony_cipci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev, 55862306a36Sopenharmony_ci struct uart_8250_port *port, int idx) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci const struct exar8250_platform *platform = exar_get_platform(); 56162306a36Sopenharmony_ci unsigned int offset = idx * 0x400; 56262306a36Sopenharmony_ci unsigned int baud = 7812500; 56362306a36Sopenharmony_ci u8 __iomem *p; 56462306a36Sopenharmony_ci int ret; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci port->port.uartclk = baud * 16; 56762306a36Sopenharmony_ci port->port.rs485_config = platform->rs485_config; 56862306a36Sopenharmony_ci port->port.rs485_supported = *(platform->rs485_supported); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /* 57162306a36Sopenharmony_ci * Setup the UART clock for the devices on expansion slot to 57262306a36Sopenharmony_ci * half the clock speed of the main chip (which is 125MHz) 57362306a36Sopenharmony_ci */ 57462306a36Sopenharmony_ci if (idx >= 8) 57562306a36Sopenharmony_ci port->port.uartclk /= 2; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci ret = default_setup(priv, pcidev, idx, offset, port); 57862306a36Sopenharmony_ci if (ret) 57962306a36Sopenharmony_ci return ret; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci p = port->port.membase; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci writeb(0x00, p + UART_EXAR_8XMODE); 58462306a36Sopenharmony_ci writeb(UART_FCTR_EXAR_TRGD, p + UART_EXAR_FCTR); 58562306a36Sopenharmony_ci writeb(128, p + UART_EXAR_TXTRG); 58662306a36Sopenharmony_ci writeb(128, p + UART_EXAR_RXTRG); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (idx == 0) { 58962306a36Sopenharmony_ci /* Setup Multipurpose Input/Output pins. */ 59062306a36Sopenharmony_ci setup_gpio(pcidev, p); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci ret = platform->register_gpio(pcidev, port); 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci return ret; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic void pci_xr17v35x_exit(struct pci_dev *pcidev) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci const struct exar8250_platform *platform = exar_get_platform(); 60162306a36Sopenharmony_ci struct exar8250 *priv = pci_get_drvdata(pcidev); 60262306a36Sopenharmony_ci struct uart_8250_port *port = serial8250_get_port(priv->line[0]); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci platform->unregister_gpio(port); 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic inline void exar_misc_clear(struct exar8250 *priv) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci /* Clear all PCI interrupts by reading INT0. No effect on IIR */ 61062306a36Sopenharmony_ci readb(priv->virt + UART_EXAR_INT0); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* Clear INT0 for Expansion Interface slave ports, too */ 61362306a36Sopenharmony_ci if (priv->board->num_ports > 8) 61462306a36Sopenharmony_ci readb(priv->virt + 0x2000 + UART_EXAR_INT0); 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci/* 61862306a36Sopenharmony_ci * These Exar UARTs have an extra interrupt indicator that could fire for a 61962306a36Sopenharmony_ci * few interrupts that are not presented/cleared through IIR. One of which is 62062306a36Sopenharmony_ci * a wakeup interrupt when coming out of sleep. These interrupts are only 62162306a36Sopenharmony_ci * cleared by reading global INT0 or INT1 registers as interrupts are 62262306a36Sopenharmony_ci * associated with channel 0. The INT[3:0] registers _are_ accessible from each 62362306a36Sopenharmony_ci * channel's address space, but for the sake of bus efficiency we register a 62462306a36Sopenharmony_ci * dedicated handler at the PCI device level to handle them. 62562306a36Sopenharmony_ci */ 62662306a36Sopenharmony_cistatic irqreturn_t exar_misc_handler(int irq, void *data) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci exar_misc_clear(data); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci return IRQ_HANDLED; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic int 63462306a36Sopenharmony_ciexar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci unsigned int nr_ports, i, bar = 0, maxnr; 63762306a36Sopenharmony_ci struct exar8250_board *board; 63862306a36Sopenharmony_ci struct uart_8250_port uart; 63962306a36Sopenharmony_ci struct exar8250 *priv; 64062306a36Sopenharmony_ci int rc; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci board = (struct exar8250_board *)ent->driver_data; 64362306a36Sopenharmony_ci if (!board) 64462306a36Sopenharmony_ci return -EINVAL; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci rc = pcim_enable_device(pcidev); 64762306a36Sopenharmony_ci if (rc) 64862306a36Sopenharmony_ci return rc; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci maxnr = pci_resource_len(pcidev, bar) >> (board->reg_shift + 3); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if (pcidev->vendor == PCI_VENDOR_ID_ACCESSIO) 65362306a36Sopenharmony_ci nr_ports = BIT(((pcidev->device & 0x38) >> 3) - 1); 65462306a36Sopenharmony_ci else if (board->num_ports) 65562306a36Sopenharmony_ci nr_ports = board->num_ports; 65662306a36Sopenharmony_ci else if (pcidev->vendor == PCI_VENDOR_ID_SEALEVEL) 65762306a36Sopenharmony_ci nr_ports = pcidev->device & 0xff; 65862306a36Sopenharmony_ci else 65962306a36Sopenharmony_ci nr_ports = pcidev->device & 0x0f; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci priv = devm_kzalloc(&pcidev->dev, struct_size(priv, line, nr_ports), GFP_KERNEL); 66262306a36Sopenharmony_ci if (!priv) 66362306a36Sopenharmony_ci return -ENOMEM; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci priv->board = board; 66662306a36Sopenharmony_ci priv->virt = pcim_iomap(pcidev, bar, 0); 66762306a36Sopenharmony_ci if (!priv->virt) 66862306a36Sopenharmony_ci return -ENOMEM; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci pci_set_master(pcidev); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci rc = pci_alloc_irq_vectors(pcidev, 1, 1, PCI_IRQ_ALL_TYPES); 67362306a36Sopenharmony_ci if (rc < 0) 67462306a36Sopenharmony_ci return rc; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci memset(&uart, 0, sizeof(uart)); 67762306a36Sopenharmony_ci uart.port.flags = UPF_SHARE_IRQ | UPF_EXAR_EFR | UPF_FIXED_TYPE | UPF_FIXED_PORT; 67862306a36Sopenharmony_ci uart.port.irq = pci_irq_vector(pcidev, 0); 67962306a36Sopenharmony_ci uart.port.dev = &pcidev->dev; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci rc = devm_request_irq(&pcidev->dev, uart.port.irq, exar_misc_handler, 68262306a36Sopenharmony_ci IRQF_SHARED, "exar_uart", priv); 68362306a36Sopenharmony_ci if (rc) 68462306a36Sopenharmony_ci return rc; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* Clear interrupts */ 68762306a36Sopenharmony_ci exar_misc_clear(priv); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci for (i = 0; i < nr_ports && i < maxnr; i++) { 69062306a36Sopenharmony_ci rc = board->setup(priv, pcidev, &uart, i); 69162306a36Sopenharmony_ci if (rc) { 69262306a36Sopenharmony_ci dev_err(&pcidev->dev, "Failed to setup port %u\n", i); 69362306a36Sopenharmony_ci break; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci dev_dbg(&pcidev->dev, "Setup PCI port: port %lx, irq %d, type %d\n", 69762306a36Sopenharmony_ci uart.port.iobase, uart.port.irq, uart.port.iotype); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci priv->line[i] = serial8250_register_8250_port(&uart); 70062306a36Sopenharmony_ci if (priv->line[i] < 0) { 70162306a36Sopenharmony_ci dev_err(&pcidev->dev, 70262306a36Sopenharmony_ci "Couldn't register serial port %lx, irq %d, type %d, error %d\n", 70362306a36Sopenharmony_ci uart.port.iobase, uart.port.irq, 70462306a36Sopenharmony_ci uart.port.iotype, priv->line[i]); 70562306a36Sopenharmony_ci break; 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci priv->nr = i; 70962306a36Sopenharmony_ci pci_set_drvdata(pcidev, priv); 71062306a36Sopenharmony_ci return 0; 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic void exar_pci_remove(struct pci_dev *pcidev) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci struct exar8250 *priv = pci_get_drvdata(pcidev); 71662306a36Sopenharmony_ci unsigned int i; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci for (i = 0; i < priv->nr; i++) 71962306a36Sopenharmony_ci serial8250_unregister_port(priv->line[i]); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci /* Ensure that every init quirk is properly torn down */ 72262306a36Sopenharmony_ci if (priv->board->exit) 72362306a36Sopenharmony_ci priv->board->exit(pcidev); 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic int __maybe_unused exar_suspend(struct device *dev) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci struct pci_dev *pcidev = to_pci_dev(dev); 72962306a36Sopenharmony_ci struct exar8250 *priv = pci_get_drvdata(pcidev); 73062306a36Sopenharmony_ci unsigned int i; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci for (i = 0; i < priv->nr; i++) 73362306a36Sopenharmony_ci if (priv->line[i] >= 0) 73462306a36Sopenharmony_ci serial8250_suspend_port(priv->line[i]); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci return 0; 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cistatic int __maybe_unused exar_resume(struct device *dev) 74062306a36Sopenharmony_ci{ 74162306a36Sopenharmony_ci struct exar8250 *priv = dev_get_drvdata(dev); 74262306a36Sopenharmony_ci unsigned int i; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci exar_misc_clear(priv); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci for (i = 0; i < priv->nr; i++) 74762306a36Sopenharmony_ci if (priv->line[i] >= 0) 74862306a36Sopenharmony_ci serial8250_resume_port(priv->line[i]); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci return 0; 75162306a36Sopenharmony_ci} 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(exar_pci_pm, exar_suspend, exar_resume); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic const struct exar8250_board pbn_fastcom335_2 = { 75662306a36Sopenharmony_ci .num_ports = 2, 75762306a36Sopenharmony_ci .setup = pci_fastcom335_setup, 75862306a36Sopenharmony_ci}; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_cistatic const struct exar8250_board pbn_fastcom335_4 = { 76162306a36Sopenharmony_ci .num_ports = 4, 76262306a36Sopenharmony_ci .setup = pci_fastcom335_setup, 76362306a36Sopenharmony_ci}; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cistatic const struct exar8250_board pbn_fastcom335_8 = { 76662306a36Sopenharmony_ci .num_ports = 8, 76762306a36Sopenharmony_ci .setup = pci_fastcom335_setup, 76862306a36Sopenharmony_ci}; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_cistatic const struct exar8250_board pbn_connect = { 77162306a36Sopenharmony_ci .setup = pci_connect_tech_setup, 77262306a36Sopenharmony_ci}; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic const struct exar8250_board pbn_exar_ibm_saturn = { 77562306a36Sopenharmony_ci .num_ports = 1, 77662306a36Sopenharmony_ci .setup = pci_xr17c154_setup, 77762306a36Sopenharmony_ci}; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cistatic const struct exar8250_board pbn_exar_XR17C15x = { 78062306a36Sopenharmony_ci .setup = pci_xr17c154_setup, 78162306a36Sopenharmony_ci}; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic const struct exar8250_board pbn_exar_XR17V35x = { 78462306a36Sopenharmony_ci .setup = pci_xr17v35x_setup, 78562306a36Sopenharmony_ci .exit = pci_xr17v35x_exit, 78662306a36Sopenharmony_ci}; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cistatic const struct exar8250_board pbn_fastcom35x_2 = { 78962306a36Sopenharmony_ci .num_ports = 2, 79062306a36Sopenharmony_ci .setup = pci_xr17v35x_setup, 79162306a36Sopenharmony_ci .exit = pci_xr17v35x_exit, 79262306a36Sopenharmony_ci}; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cistatic const struct exar8250_board pbn_fastcom35x_4 = { 79562306a36Sopenharmony_ci .num_ports = 4, 79662306a36Sopenharmony_ci .setup = pci_xr17v35x_setup, 79762306a36Sopenharmony_ci .exit = pci_xr17v35x_exit, 79862306a36Sopenharmony_ci}; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_cistatic const struct exar8250_board pbn_fastcom35x_8 = { 80162306a36Sopenharmony_ci .num_ports = 8, 80262306a36Sopenharmony_ci .setup = pci_xr17v35x_setup, 80362306a36Sopenharmony_ci .exit = pci_xr17v35x_exit, 80462306a36Sopenharmony_ci}; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cistatic const struct exar8250_board pbn_exar_XR17V4358 = { 80762306a36Sopenharmony_ci .num_ports = 12, 80862306a36Sopenharmony_ci .setup = pci_xr17v35x_setup, 80962306a36Sopenharmony_ci .exit = pci_xr17v35x_exit, 81062306a36Sopenharmony_ci}; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cistatic const struct exar8250_board pbn_exar_XR17V8358 = { 81362306a36Sopenharmony_ci .num_ports = 16, 81462306a36Sopenharmony_ci .setup = pci_xr17v35x_setup, 81562306a36Sopenharmony_ci .exit = pci_xr17v35x_exit, 81662306a36Sopenharmony_ci}; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci#define CONNECT_DEVICE(devid, sdevid, bd) { \ 81962306a36Sopenharmony_ci PCI_DEVICE_SUB( \ 82062306a36Sopenharmony_ci PCI_VENDOR_ID_EXAR, \ 82162306a36Sopenharmony_ci PCI_DEVICE_ID_EXAR_##devid, \ 82262306a36Sopenharmony_ci PCI_SUBVENDOR_ID_CONNECT_TECH, \ 82362306a36Sopenharmony_ci PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_##sdevid), 0, 0, \ 82462306a36Sopenharmony_ci (kernel_ulong_t)&bd \ 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci#define EXAR_DEVICE(vend, devid, bd) { PCI_DEVICE_DATA(vend, devid, &bd) } 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci#define IBM_DEVICE(devid, sdevid, bd) { \ 83062306a36Sopenharmony_ci PCI_DEVICE_SUB( \ 83162306a36Sopenharmony_ci PCI_VENDOR_ID_EXAR, \ 83262306a36Sopenharmony_ci PCI_DEVICE_ID_EXAR_##devid, \ 83362306a36Sopenharmony_ci PCI_VENDOR_ID_IBM, \ 83462306a36Sopenharmony_ci PCI_SUBDEVICE_ID_IBM_##sdevid), 0, 0, \ 83562306a36Sopenharmony_ci (kernel_ulong_t)&bd \ 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci#define USR_DEVICE(devid, sdevid, bd) { \ 83962306a36Sopenharmony_ci PCI_DEVICE_SUB( \ 84062306a36Sopenharmony_ci PCI_VENDOR_ID_USR, \ 84162306a36Sopenharmony_ci PCI_DEVICE_ID_EXAR_##devid, \ 84262306a36Sopenharmony_ci PCI_VENDOR_ID_EXAR, \ 84362306a36Sopenharmony_ci PCI_SUBDEVICE_ID_USR_##sdevid), 0, 0, \ 84462306a36Sopenharmony_ci (kernel_ulong_t)&bd \ 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_cistatic const struct pci_device_id exar_pci_tbl[] = { 84862306a36Sopenharmony_ci EXAR_DEVICE(ACCESSIO, COM_2S, pbn_exar_XR17C15x), 84962306a36Sopenharmony_ci EXAR_DEVICE(ACCESSIO, COM_4S, pbn_exar_XR17C15x), 85062306a36Sopenharmony_ci EXAR_DEVICE(ACCESSIO, COM_8S, pbn_exar_XR17C15x), 85162306a36Sopenharmony_ci EXAR_DEVICE(ACCESSIO, COM232_8, pbn_exar_XR17C15x), 85262306a36Sopenharmony_ci EXAR_DEVICE(ACCESSIO, COM_2SM, pbn_exar_XR17C15x), 85362306a36Sopenharmony_ci EXAR_DEVICE(ACCESSIO, COM_4SM, pbn_exar_XR17C15x), 85462306a36Sopenharmony_ci EXAR_DEVICE(ACCESSIO, COM_8SM, pbn_exar_XR17C15x), 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci CONNECT_DEVICE(XR17C152, UART_2_232, pbn_connect), 85762306a36Sopenharmony_ci CONNECT_DEVICE(XR17C154, UART_4_232, pbn_connect), 85862306a36Sopenharmony_ci CONNECT_DEVICE(XR17C158, UART_8_232, pbn_connect), 85962306a36Sopenharmony_ci CONNECT_DEVICE(XR17C152, UART_1_1, pbn_connect), 86062306a36Sopenharmony_ci CONNECT_DEVICE(XR17C154, UART_2_2, pbn_connect), 86162306a36Sopenharmony_ci CONNECT_DEVICE(XR17C158, UART_4_4, pbn_connect), 86262306a36Sopenharmony_ci CONNECT_DEVICE(XR17C152, UART_2, pbn_connect), 86362306a36Sopenharmony_ci CONNECT_DEVICE(XR17C154, UART_4, pbn_connect), 86462306a36Sopenharmony_ci CONNECT_DEVICE(XR17C158, UART_8, pbn_connect), 86562306a36Sopenharmony_ci CONNECT_DEVICE(XR17C152, UART_2_485, pbn_connect), 86662306a36Sopenharmony_ci CONNECT_DEVICE(XR17C154, UART_4_485, pbn_connect), 86762306a36Sopenharmony_ci CONNECT_DEVICE(XR17C158, UART_8_485, pbn_connect), 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci IBM_DEVICE(XR17C152, SATURN_SERIAL_ONE_PORT, pbn_exar_ibm_saturn), 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci /* USRobotics USR298x-OEM PCI Modems */ 87262306a36Sopenharmony_ci USR_DEVICE(XR17C152, 2980, pbn_exar_XR17C15x), 87362306a36Sopenharmony_ci USR_DEVICE(XR17C152, 2981, pbn_exar_XR17C15x), 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci /* Exar Corp. XR17C15[248] Dual/Quad/Octal UART */ 87662306a36Sopenharmony_ci EXAR_DEVICE(EXAR, XR17C152, pbn_exar_XR17C15x), 87762306a36Sopenharmony_ci EXAR_DEVICE(EXAR, XR17C154, pbn_exar_XR17C15x), 87862306a36Sopenharmony_ci EXAR_DEVICE(EXAR, XR17C158, pbn_exar_XR17C15x), 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci /* Exar Corp. XR17V[48]35[248] Dual/Quad/Octal/Hexa PCIe UARTs */ 88162306a36Sopenharmony_ci EXAR_DEVICE(EXAR, XR17V352, pbn_exar_XR17V35x), 88262306a36Sopenharmony_ci EXAR_DEVICE(EXAR, XR17V354, pbn_exar_XR17V35x), 88362306a36Sopenharmony_ci EXAR_DEVICE(EXAR, XR17V358, pbn_exar_XR17V35x), 88462306a36Sopenharmony_ci EXAR_DEVICE(EXAR, XR17V4358, pbn_exar_XR17V4358), 88562306a36Sopenharmony_ci EXAR_DEVICE(EXAR, XR17V8358, pbn_exar_XR17V8358), 88662306a36Sopenharmony_ci EXAR_DEVICE(COMMTECH, 4222PCIE, pbn_fastcom35x_2), 88762306a36Sopenharmony_ci EXAR_DEVICE(COMMTECH, 4224PCIE, pbn_fastcom35x_4), 88862306a36Sopenharmony_ci EXAR_DEVICE(COMMTECH, 4228PCIE, pbn_fastcom35x_8), 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci EXAR_DEVICE(COMMTECH, 4222PCI335, pbn_fastcom335_2), 89162306a36Sopenharmony_ci EXAR_DEVICE(COMMTECH, 4224PCI335, pbn_fastcom335_4), 89262306a36Sopenharmony_ci EXAR_DEVICE(COMMTECH, 2324PCI335, pbn_fastcom335_4), 89362306a36Sopenharmony_ci EXAR_DEVICE(COMMTECH, 2328PCI335, pbn_fastcom335_8), 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci EXAR_DEVICE(SEALEVEL, 710xC, pbn_exar_XR17V35x), 89662306a36Sopenharmony_ci EXAR_DEVICE(SEALEVEL, 720xC, pbn_exar_XR17V35x), 89762306a36Sopenharmony_ci EXAR_DEVICE(SEALEVEL, 740xC, pbn_exar_XR17V35x), 89862306a36Sopenharmony_ci EXAR_DEVICE(SEALEVEL, 780xC, pbn_exar_XR17V35x), 89962306a36Sopenharmony_ci EXAR_DEVICE(SEALEVEL, 716xC, pbn_exar_XR17V35x), 90062306a36Sopenharmony_ci { 0, } 90162306a36Sopenharmony_ci}; 90262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, exar_pci_tbl); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_cistatic struct pci_driver exar_pci_driver = { 90562306a36Sopenharmony_ci .name = "exar_serial", 90662306a36Sopenharmony_ci .probe = exar_pci_probe, 90762306a36Sopenharmony_ci .remove = exar_pci_remove, 90862306a36Sopenharmony_ci .driver = { 90962306a36Sopenharmony_ci .pm = &exar_pci_pm, 91062306a36Sopenharmony_ci }, 91162306a36Sopenharmony_ci .id_table = exar_pci_tbl, 91262306a36Sopenharmony_ci}; 91362306a36Sopenharmony_cimodule_pci_driver(exar_pci_driver); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 91662306a36Sopenharmony_ciMODULE_DESCRIPTION("Exar Serial Driver"); 91762306a36Sopenharmony_ciMODULE_AUTHOR("Sudip Mukherjee <sudip.mukherjee@codethink.co.uk>"); 918