162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Driver for Pericom UART */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/bits.h> 562306a36Sopenharmony_ci#include <linux/module.h> 662306a36Sopenharmony_ci#include <linux/overflow.h> 762306a36Sopenharmony_ci#include <linux/pci.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "8250.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM_2SDB 0x1051 1262306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_2S 0x1053 1362306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM422_4 0x105a 1462306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM485_4 0x105b 1562306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM_4SDB 0x105c 1662306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_4S 0x105e 1762306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM422_8 0x106a 1862306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM485_8 0x106b 1962306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_2DB 0x1091 2062306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_MPCIE_COM232_2 0x1093 2162306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_4 0x1098 2262306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_4DB 0x1099 2362306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_MPCIE_COM232_4 0x109b 2462306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_8 0x10a9 2562306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM_2SMDB 0x10d1 2662306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_2SM 0x10d3 2762306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM_4SM 0x10d9 2862306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM_4SMDB 0x10da 2962306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_4SM 0x10dc 3062306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM_8SM 0x10e9 3162306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM485_1 0x1108 3262306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM422_2 0x1110 3362306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM485_2 0x1111 3462306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM422_4 0x1118 3562306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM485_4 0x1119 3662306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_2S 0x1152 3762306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_4S 0x115a 3862306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_PCIE_ICM232_2 0x1190 3962306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM232_2 0x1191 4062306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_PCIE_ICM232_4 0x1198 4162306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM232_4 0x1199 4262306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_2SM 0x11d0 4362306a36Sopenharmony_ci#define PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_4SM 0x11d8 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistruct pericom8250 { 4662306a36Sopenharmony_ci void __iomem *virt; 4762306a36Sopenharmony_ci unsigned int nr; 4862306a36Sopenharmony_ci int line[]; 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic void pericom_do_set_divisor(struct uart_port *port, unsigned int baud, 5262306a36Sopenharmony_ci unsigned int quot, unsigned int quot_frac) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci int scr; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci for (scr = 16; scr > 4; scr--) { 5762306a36Sopenharmony_ci unsigned int maxrate = port->uartclk / scr; 5862306a36Sopenharmony_ci unsigned int divisor = max(maxrate / baud, 1U); 5962306a36Sopenharmony_ci int delta = maxrate / divisor - baud; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (baud > maxrate + baud / 50) 6262306a36Sopenharmony_ci continue; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (delta > baud / 50) 6562306a36Sopenharmony_ci divisor++; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (divisor > 0xffff) 6862306a36Sopenharmony_ci continue; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* Update delta due to possible divisor change */ 7162306a36Sopenharmony_ci delta = maxrate / divisor - baud; 7262306a36Sopenharmony_ci if (abs(delta) < baud / 50) { 7362306a36Sopenharmony_ci struct uart_8250_port *up = up_to_u8250p(port); 7462306a36Sopenharmony_ci int lcr = serial_port_in(port, UART_LCR); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci serial_port_out(port, UART_LCR, lcr | UART_LCR_DLAB); 7762306a36Sopenharmony_ci serial_dl_write(up, divisor); 7862306a36Sopenharmony_ci serial_port_out(port, 2, 16 - scr); 7962306a36Sopenharmony_ci serial_port_out(port, UART_LCR, lcr); 8062306a36Sopenharmony_ci return; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic int pericom8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci unsigned int nr, i, bar = 0, maxnr; 8862306a36Sopenharmony_ci struct pericom8250 *pericom; 8962306a36Sopenharmony_ci struct uart_8250_port uart; 9062306a36Sopenharmony_ci int ret; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci ret = pcim_enable_device(pdev); 9362306a36Sopenharmony_ci if (ret) 9462306a36Sopenharmony_ci return ret; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci maxnr = pci_resource_len(pdev, bar) >> 3; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (pdev->vendor == PCI_VENDOR_ID_PERICOM) 9962306a36Sopenharmony_ci nr = pdev->device & 0x0f; 10062306a36Sopenharmony_ci else if (pdev->vendor == PCI_VENDOR_ID_ACCESSIO) 10162306a36Sopenharmony_ci nr = BIT(((pdev->device & 0x38) >> 3) - 1); 10262306a36Sopenharmony_ci else 10362306a36Sopenharmony_ci nr = 1; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci pericom = devm_kzalloc(&pdev->dev, struct_size(pericom, line, nr), GFP_KERNEL); 10662306a36Sopenharmony_ci if (!pericom) 10762306a36Sopenharmony_ci return -ENOMEM; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci pericom->virt = pcim_iomap(pdev, bar, 0); 11062306a36Sopenharmony_ci if (!pericom->virt) 11162306a36Sopenharmony_ci return -ENOMEM; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci memset(&uart, 0, sizeof(uart)); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci uart.port.dev = &pdev->dev; 11662306a36Sopenharmony_ci uart.port.irq = pdev->irq; 11762306a36Sopenharmony_ci uart.port.private_data = pericom; 11862306a36Sopenharmony_ci uart.port.iotype = UPIO_PORT; 11962306a36Sopenharmony_ci uart.port.uartclk = 921600 * 16; 12062306a36Sopenharmony_ci uart.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; 12162306a36Sopenharmony_ci uart.port.set_divisor = pericom_do_set_divisor; 12262306a36Sopenharmony_ci for (i = 0; i < nr && i < maxnr; i++) { 12362306a36Sopenharmony_ci unsigned int offset = (i == 3 && nr == 4) ? 0x38 : i * 0x8; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci uart.port.iobase = pci_resource_start(pdev, bar) + offset; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Setup PCI port: port %lx, irq %d, type %d\n", 12862306a36Sopenharmony_ci uart.port.iobase, uart.port.irq, uart.port.iotype); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci pericom->line[i] = serial8250_register_8250_port(&uart); 13162306a36Sopenharmony_ci if (pericom->line[i] < 0) { 13262306a36Sopenharmony_ci dev_err(&pdev->dev, 13362306a36Sopenharmony_ci "Couldn't register serial port %lx, irq %d, type %d, error %d\n", 13462306a36Sopenharmony_ci uart.port.iobase, uart.port.irq, 13562306a36Sopenharmony_ci uart.port.iotype, pericom->line[i]); 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci pericom->nr = i; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci pci_set_drvdata(pdev, pericom); 14262306a36Sopenharmony_ci return 0; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic void pericom8250_remove(struct pci_dev *pdev) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct pericom8250 *pericom = pci_get_drvdata(pdev); 14862306a36Sopenharmony_ci unsigned int i; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci for (i = 0; i < pericom->nr; i++) 15162306a36Sopenharmony_ci serial8250_unregister_port(pericom->line[i]); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic const struct pci_device_id pericom8250_pci_ids[] = { 15562306a36Sopenharmony_ci /* 15662306a36Sopenharmony_ci * Pericom PI7C9X795[1248] Uno/Dual/Quad/Octal UART 15762306a36Sopenharmony_ci * (Only 7954 has an offset jump for port 4) 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_ci { PCI_VDEVICE(PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7951) }, 16062306a36Sopenharmony_ci { PCI_VDEVICE(PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7952) }, 16162306a36Sopenharmony_ci { PCI_VDEVICE(PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7954) }, 16262306a36Sopenharmony_ci { PCI_VDEVICE(PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7958) }, 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* 16562306a36Sopenharmony_ci * ACCES I/O Products quad 16662306a36Sopenharmony_ci * (Only 7954 has an offset jump for port 4) 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM_2SDB) }, 16962306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_2S) }, 17062306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM422_4) }, 17162306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM485_4) }, 17262306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM_4SDB) }, 17362306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_4S) }, 17462306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM422_8) }, 17562306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM485_8) }, 17662306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_2DB) }, 17762306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_COM232_2) }, 17862306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_4) }, 17962306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_4DB) }, 18062306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_COM232_4) }, 18162306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_8) }, 18262306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM_2SMDB) }, 18362306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_2SM) }, 18462306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM_4SM) }, 18562306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM_4SMDB) }, 18662306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_4SM) }, 18762306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM_8SM) }, 18862306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM485_1) }, 18962306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM422_2) }, 19062306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM485_2) }, 19162306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM422_4) }, 19262306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM485_4) }, 19362306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_2S) }, 19462306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_4S) }, 19562306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_ICM232_2) }, 19662306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM232_2) }, 19762306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_ICM232_4) }, 19862306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM232_4) }, 19962306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_2SM) }, 20062306a36Sopenharmony_ci { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_4SM) }, 20162306a36Sopenharmony_ci { } 20262306a36Sopenharmony_ci}; 20362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pericom8250_pci_ids); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic struct pci_driver pericom8250_pci_driver = { 20662306a36Sopenharmony_ci .name = "8250_pericom", 20762306a36Sopenharmony_ci .id_table = pericom8250_pci_ids, 20862306a36Sopenharmony_ci .probe = pericom8250_probe, 20962306a36Sopenharmony_ci .remove = pericom8250_remove, 21062306a36Sopenharmony_ci}; 21162306a36Sopenharmony_cimodule_pci_driver(pericom8250_pci_driver); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 21462306a36Sopenharmony_ciMODULE_DESCRIPTION("Pericom UART driver"); 215