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 2611361610Skuldip dwivedi struct fsl8250_data { 2711361610Skuldip dwivedi int line; 2811361610Skuldip dwivedi }; 2911361610Skuldip dwivedi 309bef3d41SPaul Gortmaker int fsl8250_handle_irq(struct uart_port *port) 319bef3d41SPaul Gortmaker { 329bef3d41SPaul Gortmaker unsigned char lsr, orig_lsr; 339bef3d41SPaul Gortmaker unsigned int iir; 34b1261c86SAndy Shevchenko struct uart_8250_port *up = up_to_u8250p(port); 359bef3d41SPaul Gortmaker 36*75f4e830SJohan Hovold spin_lock(&up->port.lock); 379bef3d41SPaul Gortmaker 389bef3d41SPaul Gortmaker iir = port->serial_in(port, UART_IIR); 399bef3d41SPaul Gortmaker if (iir & UART_IIR_NO_INT) { 40*75f4e830SJohan Hovold spin_unlock(&up->port.lock); 419bef3d41SPaul Gortmaker return 0; 429bef3d41SPaul Gortmaker } 439bef3d41SPaul Gortmaker 449bef3d41SPaul Gortmaker /* This is the WAR; if last event was BRK, then read and return */ 459bef3d41SPaul Gortmaker if (unlikely(up->lsr_saved_flags & UART_LSR_BI)) { 469bef3d41SPaul Gortmaker up->lsr_saved_flags &= ~UART_LSR_BI; 479bef3d41SPaul Gortmaker port->serial_in(port, UART_RX); 48*75f4e830SJohan Hovold spin_unlock(&up->port.lock); 499bef3d41SPaul Gortmaker return 1; 509bef3d41SPaul Gortmaker } 519bef3d41SPaul Gortmaker 529bef3d41SPaul Gortmaker lsr = orig_lsr = up->port.serial_in(&up->port, UART_LSR); 539bef3d41SPaul Gortmaker 546d7f677aSDarwin Dingel /* Process incoming characters first */ 556d7f677aSDarwin Dingel if ((lsr & (UART_LSR_DR | UART_LSR_BI)) && 566d7f677aSDarwin Dingel (up->ier & (UART_IER_RLSI | UART_IER_RDI))) { 579bef3d41SPaul Gortmaker lsr = serial8250_rx_chars(up, lsr); 586d7f677aSDarwin Dingel } 596d7f677aSDarwin Dingel 606d7f677aSDarwin Dingel /* Stop processing interrupts on input overrun */ 616d7f677aSDarwin Dingel if ((orig_lsr & UART_LSR_OE) && (up->overrun_backoff_time_ms > 0)) { 626d7f677aSDarwin Dingel unsigned long delay; 636d7f677aSDarwin Dingel 646d7f677aSDarwin Dingel up->ier = port->serial_in(port, UART_IER); 656d7f677aSDarwin Dingel if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) { 666d7f677aSDarwin Dingel port->ops->stop_rx(port); 676d7f677aSDarwin Dingel } else { 686d7f677aSDarwin Dingel /* Keep restarting the timer until 696d7f677aSDarwin Dingel * the input overrun subsides. 706d7f677aSDarwin Dingel */ 716d7f677aSDarwin Dingel cancel_delayed_work(&up->overrun_backoff); 726d7f677aSDarwin Dingel } 736d7f677aSDarwin Dingel 746d7f677aSDarwin Dingel delay = msecs_to_jiffies(up->overrun_backoff_time_ms); 756d7f677aSDarwin Dingel schedule_delayed_work(&up->overrun_backoff, delay); 766d7f677aSDarwin Dingel } 779bef3d41SPaul Gortmaker 789bef3d41SPaul Gortmaker serial8250_modem_status(up); 799bef3d41SPaul Gortmaker 80409cc454SAndrij Abyzov if ((lsr & UART_LSR_THRE) && (up->ier & UART_IER_THRI)) 819bef3d41SPaul Gortmaker serial8250_tx_chars(up); 829bef3d41SPaul Gortmaker 839bef3d41SPaul Gortmaker up->lsr_saved_flags = orig_lsr; 84*75f4e830SJohan Hovold 85*75f4e830SJohan Hovold uart_unlock_and_check_sysrq(&up->port); 86*75f4e830SJohan Hovold 879bef3d41SPaul Gortmaker return 1; 889bef3d41SPaul Gortmaker } 89bd63acf9SArnd Bergmann EXPORT_SYMBOL_GPL(fsl8250_handle_irq); 9011361610Skuldip dwivedi 9111361610Skuldip dwivedi #ifdef CONFIG_ACPI 9211361610Skuldip dwivedi static int fsl8250_acpi_probe(struct platform_device *pdev) 9311361610Skuldip dwivedi { 9411361610Skuldip dwivedi struct fsl8250_data *data; 9511361610Skuldip dwivedi struct uart_8250_port port8250; 9611361610Skuldip dwivedi struct device *dev = &pdev->dev; 9711361610Skuldip dwivedi struct resource *regs; 9811361610Skuldip dwivedi 9911361610Skuldip dwivedi int ret, irq; 10011361610Skuldip dwivedi 10111361610Skuldip dwivedi regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 10211361610Skuldip dwivedi if (!regs) { 10311361610Skuldip dwivedi dev_err(dev, "no registers defined\n"); 10411361610Skuldip dwivedi return -EINVAL; 10511361610Skuldip dwivedi } 10611361610Skuldip dwivedi 10711361610Skuldip dwivedi irq = platform_get_irq(pdev, 0); 108b9edc682SWang Qing if (irq < 0) 10911361610Skuldip dwivedi return irq; 11011361610Skuldip dwivedi 11111361610Skuldip dwivedi memset(&port8250, 0, sizeof(port8250)); 11211361610Skuldip dwivedi 11311361610Skuldip dwivedi ret = device_property_read_u32(dev, "clock-frequency", 11411361610Skuldip dwivedi &port8250.port.uartclk); 11511361610Skuldip dwivedi if (ret) 11611361610Skuldip dwivedi return ret; 11711361610Skuldip dwivedi 11811361610Skuldip dwivedi spin_lock_init(&port8250.port.lock); 11911361610Skuldip dwivedi 12011361610Skuldip dwivedi port8250.port.mapbase = regs->start; 12111361610Skuldip dwivedi port8250.port.irq = irq; 12211361610Skuldip dwivedi port8250.port.handle_irq = fsl8250_handle_irq; 12311361610Skuldip dwivedi port8250.port.type = PORT_16550A; 12411361610Skuldip dwivedi port8250.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF 12511361610Skuldip dwivedi | UPF_FIXED_PORT | UPF_IOREMAP 12611361610Skuldip dwivedi | UPF_FIXED_TYPE; 12711361610Skuldip dwivedi port8250.port.dev = dev; 12811361610Skuldip dwivedi port8250.port.mapsize = resource_size(regs); 12911361610Skuldip dwivedi port8250.port.iotype = UPIO_MEM; 13011361610Skuldip dwivedi port8250.port.irqflags = IRQF_SHARED; 13111361610Skuldip dwivedi 13211361610Skuldip dwivedi port8250.port.membase = devm_ioremap(dev, port8250.port.mapbase, 13311361610Skuldip dwivedi port8250.port.mapsize); 13411361610Skuldip dwivedi if (!port8250.port.membase) 13511361610Skuldip dwivedi return -ENOMEM; 13611361610Skuldip dwivedi 13711361610Skuldip dwivedi data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 13811361610Skuldip dwivedi if (!data) 13911361610Skuldip dwivedi return -ENOMEM; 14011361610Skuldip dwivedi 14111361610Skuldip dwivedi data->line = serial8250_register_8250_port(&port8250); 14211361610Skuldip dwivedi if (data->line < 0) 14311361610Skuldip dwivedi return data->line; 14411361610Skuldip dwivedi 14511361610Skuldip dwivedi platform_set_drvdata(pdev, data); 14611361610Skuldip dwivedi return 0; 14711361610Skuldip dwivedi } 14811361610Skuldip dwivedi 14911361610Skuldip dwivedi static int fsl8250_acpi_remove(struct platform_device *pdev) 15011361610Skuldip dwivedi { 15111361610Skuldip dwivedi struct fsl8250_data *data = platform_get_drvdata(pdev); 15211361610Skuldip dwivedi 15311361610Skuldip dwivedi serial8250_unregister_port(data->line); 15411361610Skuldip dwivedi return 0; 15511361610Skuldip dwivedi } 15611361610Skuldip dwivedi 15711361610Skuldip dwivedi static const struct acpi_device_id fsl_8250_acpi_id[] = { 15811361610Skuldip dwivedi { "NXP0018", 0 }, 15911361610Skuldip dwivedi { }, 16011361610Skuldip dwivedi }; 16111361610Skuldip dwivedi MODULE_DEVICE_TABLE(acpi, fsl_8250_acpi_id); 16211361610Skuldip dwivedi 16311361610Skuldip dwivedi static struct platform_driver fsl8250_platform_driver = { 16411361610Skuldip dwivedi .driver = { 16511361610Skuldip dwivedi .name = "fsl-16550-uart", 16611361610Skuldip dwivedi .acpi_match_table = ACPI_PTR(fsl_8250_acpi_id), 16711361610Skuldip dwivedi }, 16811361610Skuldip dwivedi .probe = fsl8250_acpi_probe, 16911361610Skuldip dwivedi .remove = fsl8250_acpi_remove, 17011361610Skuldip dwivedi }; 17111361610Skuldip dwivedi 17211361610Skuldip dwivedi module_platform_driver(fsl8250_platform_driver); 17311361610Skuldip dwivedi #endif 174