1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 29bef3d41SPaul Gortmaker /* 39bef3d41SPaul Gortmaker * Freescale 16550 UART "driver", Copyright (C) 2011 Paul Gortmaker. 411361610Skuldip dwivedi * Copyright 2020 NXP 511361610Skuldip dwivedi * Copyright 2020 Puresoftware Ltd. 69bef3d41SPaul Gortmaker * 79bef3d41SPaul Gortmaker * This isn't a full driver; it just provides an alternate IRQ 811361610Skuldip dwivedi * handler to deal with an errata and provide ACPI wrapper. 911361610Skuldip dwivedi * Everything else is just using the bog standard 8250 support. 109bef3d41SPaul Gortmaker * 119bef3d41SPaul Gortmaker * We follow code flow of serial8250_default_handle_irq() but add 129bef3d41SPaul Gortmaker * a check for a break and insert a dummy read on the Rx for the 139bef3d41SPaul Gortmaker * immediately following IRQ event. 149bef3d41SPaul Gortmaker * 159bef3d41SPaul Gortmaker * We re-use the already existing "bug handling" lsr_saved_flags 169bef3d41SPaul Gortmaker * field to carry the "what we just did" information from the one 179bef3d41SPaul Gortmaker * IRQ event to the next one. 189bef3d41SPaul Gortmaker */ 199bef3d41SPaul Gortmaker 2011361610Skuldip dwivedi #include <linux/acpi.h> 2111361610Skuldip dwivedi #include <linux/serial_reg.h> 2211361610Skuldip dwivedi #include <linux/serial_8250.h> 2311361610Skuldip dwivedi 2411361610Skuldip dwivedi #include "8250.h" 2511361610Skuldip dwivedi 269bef3d41SPaul Gortmaker int fsl8250_handle_irq(struct uart_port *port) 279bef3d41SPaul Gortmaker { 28853a9ae2SJohan Hovold unsigned long flags; 29f8ba5680SIlpo Järvinen u16 lsr, orig_lsr; 309bef3d41SPaul Gortmaker unsigned int iir; 31b1261c86SAndy Shevchenko struct uart_8250_port *up = up_to_u8250p(port); 329bef3d41SPaul Gortmaker 33853a9ae2SJohan Hovold spin_lock_irqsave(&up->port.lock, flags); 349bef3d41SPaul Gortmaker 359bef3d41SPaul Gortmaker iir = port->serial_in(port, UART_IIR); 369bef3d41SPaul Gortmaker if (iir & UART_IIR_NO_INT) { 3775f4e830SJohan Hovold spin_unlock(&up->port.lock); 389bef3d41SPaul Gortmaker return 0; 399bef3d41SPaul Gortmaker } 409bef3d41SPaul Gortmaker 419bef3d41SPaul Gortmaker /* This is the WAR; if last event was BRK, then read and return */ 429bef3d41SPaul Gortmaker if (unlikely(up->lsr_saved_flags & UART_LSR_BI)) { 439bef3d41SPaul Gortmaker up->lsr_saved_flags &= ~UART_LSR_BI; 449bef3d41SPaul Gortmaker port->serial_in(port, UART_RX); 4575f4e830SJohan Hovold spin_unlock(&up->port.lock); 469bef3d41SPaul Gortmaker return 1; 479bef3d41SPaul Gortmaker } 489bef3d41SPaul Gortmaker 499bef3d41SPaul Gortmaker lsr = orig_lsr = up->port.serial_in(&up->port, UART_LSR); 509bef3d41SPaul Gortmaker 516d7f677aSDarwin Dingel /* Process incoming characters first */ 526d7f677aSDarwin Dingel if ((lsr & (UART_LSR_DR | UART_LSR_BI)) && 536d7f677aSDarwin Dingel (up->ier & (UART_IER_RLSI | UART_IER_RDI))) { 549bef3d41SPaul Gortmaker lsr = serial8250_rx_chars(up, lsr); 556d7f677aSDarwin Dingel } 566d7f677aSDarwin Dingel 576d7f677aSDarwin Dingel /* Stop processing interrupts on input overrun */ 586d7f677aSDarwin Dingel if ((orig_lsr & UART_LSR_OE) && (up->overrun_backoff_time_ms > 0)) { 596d7f677aSDarwin Dingel unsigned long delay; 606d7f677aSDarwin Dingel 616d7f677aSDarwin Dingel up->ier = port->serial_in(port, UART_IER); 626d7f677aSDarwin Dingel if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) { 636d7f677aSDarwin Dingel port->ops->stop_rx(port); 646d7f677aSDarwin Dingel } else { 656d7f677aSDarwin Dingel /* Keep restarting the timer until 666d7f677aSDarwin Dingel * the input overrun subsides. 676d7f677aSDarwin Dingel */ 686d7f677aSDarwin Dingel cancel_delayed_work(&up->overrun_backoff); 696d7f677aSDarwin Dingel } 706d7f677aSDarwin Dingel 716d7f677aSDarwin Dingel delay = msecs_to_jiffies(up->overrun_backoff_time_ms); 726d7f677aSDarwin Dingel schedule_delayed_work(&up->overrun_backoff, delay); 736d7f677aSDarwin Dingel } 749bef3d41SPaul Gortmaker 759bef3d41SPaul Gortmaker serial8250_modem_status(up); 769bef3d41SPaul Gortmaker 77409cc454SAndrij Abyzov if ((lsr & UART_LSR_THRE) && (up->ier & UART_IER_THRI)) 789bef3d41SPaul Gortmaker serial8250_tx_chars(up); 799bef3d41SPaul Gortmaker 80*9d3aacebSUwe Kleine-König up->lsr_saved_flags |= orig_lsr & UART_LSR_BI; 8175f4e830SJohan Hovold 82853a9ae2SJohan Hovold uart_unlock_and_check_sysrq_irqrestore(&up->port, flags); 8375f4e830SJohan Hovold 849bef3d41SPaul Gortmaker return 1; 859bef3d41SPaul Gortmaker } 86bd63acf9SArnd Bergmann EXPORT_SYMBOL_GPL(fsl8250_handle_irq); 8711361610Skuldip dwivedi 8811361610Skuldip dwivedi #ifdef CONFIG_ACPI 8928f5cb37SGeert Uytterhoeven struct fsl8250_data { 9028f5cb37SGeert Uytterhoeven int line; 9128f5cb37SGeert Uytterhoeven }; 9228f5cb37SGeert Uytterhoeven 9311361610Skuldip dwivedi static int fsl8250_acpi_probe(struct platform_device *pdev) 9411361610Skuldip dwivedi { 9511361610Skuldip dwivedi struct fsl8250_data *data; 9611361610Skuldip dwivedi struct uart_8250_port port8250; 9711361610Skuldip dwivedi struct device *dev = &pdev->dev; 9811361610Skuldip dwivedi struct resource *regs; 9911361610Skuldip dwivedi 10011361610Skuldip dwivedi int ret, irq; 10111361610Skuldip dwivedi 10211361610Skuldip dwivedi regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 10311361610Skuldip dwivedi if (!regs) { 10411361610Skuldip dwivedi dev_err(dev, "no registers defined\n"); 10511361610Skuldip dwivedi return -EINVAL; 10611361610Skuldip dwivedi } 10711361610Skuldip dwivedi 10811361610Skuldip dwivedi irq = platform_get_irq(pdev, 0); 109b9edc682SWang Qing if (irq < 0) 11011361610Skuldip dwivedi return irq; 11111361610Skuldip dwivedi 11211361610Skuldip dwivedi memset(&port8250, 0, sizeof(port8250)); 11311361610Skuldip dwivedi 11411361610Skuldip dwivedi ret = device_property_read_u32(dev, "clock-frequency", 11511361610Skuldip dwivedi &port8250.port.uartclk); 11611361610Skuldip dwivedi if (ret) 11711361610Skuldip dwivedi return ret; 11811361610Skuldip dwivedi 11911361610Skuldip dwivedi spin_lock_init(&port8250.port.lock); 12011361610Skuldip dwivedi 12111361610Skuldip dwivedi port8250.port.mapbase = regs->start; 12211361610Skuldip dwivedi port8250.port.irq = irq; 12311361610Skuldip dwivedi port8250.port.handle_irq = fsl8250_handle_irq; 12411361610Skuldip dwivedi port8250.port.type = PORT_16550A; 12511361610Skuldip dwivedi port8250.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF 12611361610Skuldip dwivedi | UPF_FIXED_PORT | UPF_IOREMAP 12711361610Skuldip dwivedi | UPF_FIXED_TYPE; 12811361610Skuldip dwivedi port8250.port.dev = dev; 12911361610Skuldip dwivedi port8250.port.mapsize = resource_size(regs); 13011361610Skuldip dwivedi port8250.port.iotype = UPIO_MEM; 13111361610Skuldip dwivedi port8250.port.irqflags = IRQF_SHARED; 13211361610Skuldip dwivedi 13311361610Skuldip dwivedi port8250.port.membase = devm_ioremap(dev, port8250.port.mapbase, 13411361610Skuldip dwivedi port8250.port.mapsize); 13511361610Skuldip dwivedi if (!port8250.port.membase) 13611361610Skuldip dwivedi return -ENOMEM; 13711361610Skuldip dwivedi 13811361610Skuldip dwivedi data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 13911361610Skuldip dwivedi if (!data) 14011361610Skuldip dwivedi return -ENOMEM; 14111361610Skuldip dwivedi 14211361610Skuldip dwivedi data->line = serial8250_register_8250_port(&port8250); 14311361610Skuldip dwivedi if (data->line < 0) 14411361610Skuldip dwivedi return data->line; 14511361610Skuldip dwivedi 14611361610Skuldip dwivedi platform_set_drvdata(pdev, data); 14711361610Skuldip dwivedi return 0; 14811361610Skuldip dwivedi } 14911361610Skuldip dwivedi 15011361610Skuldip dwivedi static int fsl8250_acpi_remove(struct platform_device *pdev) 15111361610Skuldip dwivedi { 15211361610Skuldip dwivedi struct fsl8250_data *data = platform_get_drvdata(pdev); 15311361610Skuldip dwivedi 15411361610Skuldip dwivedi serial8250_unregister_port(data->line); 15511361610Skuldip dwivedi return 0; 15611361610Skuldip dwivedi } 15711361610Skuldip dwivedi 15811361610Skuldip dwivedi static const struct acpi_device_id fsl_8250_acpi_id[] = { 15911361610Skuldip dwivedi { "NXP0018", 0 }, 16011361610Skuldip dwivedi { }, 16111361610Skuldip dwivedi }; 16211361610Skuldip dwivedi MODULE_DEVICE_TABLE(acpi, fsl_8250_acpi_id); 16311361610Skuldip dwivedi 16411361610Skuldip dwivedi static struct platform_driver fsl8250_platform_driver = { 16511361610Skuldip dwivedi .driver = { 16611361610Skuldip dwivedi .name = "fsl-16550-uart", 16711361610Skuldip dwivedi .acpi_match_table = ACPI_PTR(fsl_8250_acpi_id), 16811361610Skuldip dwivedi }, 16911361610Skuldip dwivedi .probe = fsl8250_acpi_probe, 17011361610Skuldip dwivedi .remove = fsl8250_acpi_remove, 17111361610Skuldip dwivedi }; 17211361610Skuldip dwivedi 17311361610Skuldip dwivedi module_platform_driver(fsl8250_platform_driver); 17411361610Skuldip dwivedi #endif 175