18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Freescale 16550 UART "driver", Copyright (C) 2011 Paul Gortmaker. 48c2ecf20Sopenharmony_ci * Copyright 2020 NXP 58c2ecf20Sopenharmony_ci * Copyright 2020 Puresoftware Ltd. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This isn't a full driver; it just provides an alternate IRQ 88c2ecf20Sopenharmony_ci * handler to deal with an errata and provide ACPI wrapper. 98c2ecf20Sopenharmony_ci * Everything else is just using the bog standard 8250 support. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * We follow code flow of serial8250_default_handle_irq() but add 128c2ecf20Sopenharmony_ci * a check for a break and insert a dummy read on the Rx for the 138c2ecf20Sopenharmony_ci * immediately following IRQ event. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * We re-use the already existing "bug handling" lsr_saved_flags 168c2ecf20Sopenharmony_ci * field to carry the "what we just did" information from the one 178c2ecf20Sopenharmony_ci * IRQ event to the next one. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/acpi.h> 218c2ecf20Sopenharmony_ci#include <linux/serial_reg.h> 228c2ecf20Sopenharmony_ci#include <linux/serial_8250.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "8250.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct fsl8250_data { 278c2ecf20Sopenharmony_ci int line; 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ciint fsl8250_handle_irq(struct uart_port *port) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci unsigned char lsr, orig_lsr; 338c2ecf20Sopenharmony_ci unsigned long flags; 348c2ecf20Sopenharmony_ci unsigned int iir; 358c2ecf20Sopenharmony_ci struct uart_8250_port *up = up_to_u8250p(port); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci spin_lock_irqsave(&up->port.lock, flags); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci iir = port->serial_in(port, UART_IIR); 408c2ecf20Sopenharmony_ci if (iir & UART_IIR_NO_INT) { 418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&up->port.lock, flags); 428c2ecf20Sopenharmony_ci return 0; 438c2ecf20Sopenharmony_ci } 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci /* This is the WAR; if last event was BRK, then read and return */ 468c2ecf20Sopenharmony_ci if (unlikely(up->lsr_saved_flags & UART_LSR_BI)) { 478c2ecf20Sopenharmony_ci up->lsr_saved_flags &= ~UART_LSR_BI; 488c2ecf20Sopenharmony_ci port->serial_in(port, UART_RX); 498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&up->port.lock, flags); 508c2ecf20Sopenharmony_ci return 1; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci lsr = orig_lsr = up->port.serial_in(&up->port, UART_LSR); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci /* Process incoming characters first */ 568c2ecf20Sopenharmony_ci if ((lsr & (UART_LSR_DR | UART_LSR_BI)) && 578c2ecf20Sopenharmony_ci (up->ier & (UART_IER_RLSI | UART_IER_RDI))) { 588c2ecf20Sopenharmony_ci lsr = serial8250_rx_chars(up, lsr); 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* Stop processing interrupts on input overrun */ 628c2ecf20Sopenharmony_ci if ((orig_lsr & UART_LSR_OE) && (up->overrun_backoff_time_ms > 0)) { 638c2ecf20Sopenharmony_ci unsigned long delay; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci up->ier = port->serial_in(port, UART_IER); 668c2ecf20Sopenharmony_ci if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) { 678c2ecf20Sopenharmony_ci port->ops->stop_rx(port); 688c2ecf20Sopenharmony_ci } else { 698c2ecf20Sopenharmony_ci /* Keep restarting the timer until 708c2ecf20Sopenharmony_ci * the input overrun subsides. 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_ci cancel_delayed_work(&up->overrun_backoff); 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci delay = msecs_to_jiffies(up->overrun_backoff_time_ms); 768c2ecf20Sopenharmony_ci schedule_delayed_work(&up->overrun_backoff, delay); 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci serial8250_modem_status(up); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if ((lsr & UART_LSR_THRE) && (up->ier & UART_IER_THRI)) 828c2ecf20Sopenharmony_ci serial8250_tx_chars(up); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci up->lsr_saved_flags = orig_lsr; 858c2ecf20Sopenharmony_ci uart_unlock_and_check_sysrq(&up->port, flags); 868c2ecf20Sopenharmony_ci return 1; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl8250_handle_irq); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI 918c2ecf20Sopenharmony_cistatic int fsl8250_acpi_probe(struct platform_device *pdev) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct fsl8250_data *data; 948c2ecf20Sopenharmony_ci struct uart_8250_port port8250; 958c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 968c2ecf20Sopenharmony_ci struct resource *regs; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci int ret, irq; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1018c2ecf20Sopenharmony_ci if (!regs) { 1028c2ecf20Sopenharmony_ci dev_err(dev, "no registers defined\n"); 1038c2ecf20Sopenharmony_ci return -EINVAL; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 1078c2ecf20Sopenharmony_ci if (irq < 0) { 1088c2ecf20Sopenharmony_ci if (irq != -EPROBE_DEFER) 1098c2ecf20Sopenharmony_ci dev_err(dev, "cannot get irq\n"); 1108c2ecf20Sopenharmony_ci return irq; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci memset(&port8250, 0, sizeof(port8250)); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci ret = device_property_read_u32(dev, "clock-frequency", 1168c2ecf20Sopenharmony_ci &port8250.port.uartclk); 1178c2ecf20Sopenharmony_ci if (ret) 1188c2ecf20Sopenharmony_ci return ret; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci spin_lock_init(&port8250.port.lock); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci port8250.port.mapbase = regs->start; 1238c2ecf20Sopenharmony_ci port8250.port.irq = irq; 1248c2ecf20Sopenharmony_ci port8250.port.handle_irq = fsl8250_handle_irq; 1258c2ecf20Sopenharmony_ci port8250.port.type = PORT_16550A; 1268c2ecf20Sopenharmony_ci port8250.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF 1278c2ecf20Sopenharmony_ci | UPF_FIXED_PORT | UPF_IOREMAP 1288c2ecf20Sopenharmony_ci | UPF_FIXED_TYPE; 1298c2ecf20Sopenharmony_ci port8250.port.dev = dev; 1308c2ecf20Sopenharmony_ci port8250.port.mapsize = resource_size(regs); 1318c2ecf20Sopenharmony_ci port8250.port.iotype = UPIO_MEM; 1328c2ecf20Sopenharmony_ci port8250.port.irqflags = IRQF_SHARED; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci port8250.port.membase = devm_ioremap(dev, port8250.port.mapbase, 1358c2ecf20Sopenharmony_ci port8250.port.mapsize); 1368c2ecf20Sopenharmony_ci if (!port8250.port.membase) 1378c2ecf20Sopenharmony_ci return -ENOMEM; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 1408c2ecf20Sopenharmony_ci if (!data) 1418c2ecf20Sopenharmony_ci return -ENOMEM; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci data->line = serial8250_register_8250_port(&port8250); 1448c2ecf20Sopenharmony_ci if (data->line < 0) 1458c2ecf20Sopenharmony_ci return data->line; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, data); 1488c2ecf20Sopenharmony_ci return 0; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int fsl8250_acpi_remove(struct platform_device *pdev) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct fsl8250_data *data = platform_get_drvdata(pdev); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci serial8250_unregister_port(data->line); 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic const struct acpi_device_id fsl_8250_acpi_id[] = { 1608c2ecf20Sopenharmony_ci { "NXP0018", 0 }, 1618c2ecf20Sopenharmony_ci { }, 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, fsl_8250_acpi_id); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic struct platform_driver fsl8250_platform_driver = { 1668c2ecf20Sopenharmony_ci .driver = { 1678c2ecf20Sopenharmony_ci .name = "fsl-16550-uart", 1688c2ecf20Sopenharmony_ci .acpi_match_table = ACPI_PTR(fsl_8250_acpi_id), 1698c2ecf20Sopenharmony_ci }, 1708c2ecf20Sopenharmony_ci .probe = fsl8250_acpi_probe, 1718c2ecf20Sopenharmony_ci .remove = fsl8250_acpi_remove, 1728c2ecf20Sopenharmony_ci}; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cimodule_platform_driver(fsl8250_platform_driver); 1758c2ecf20Sopenharmony_ci#endif 176