linux/drivers/tty/serial/8250/8250_fsl.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Freescale 16550 UART "driver", Copyright (C) 2011 Paul Gortmaker.
   4 * Copyright 2020 NXP
   5 * Copyright 2020 Puresoftware Ltd.
   6 *
   7 * This isn't a full driver; it just provides an alternate IRQ
   8 * handler to deal with an errata and provide ACPI wrapper.
   9 * Everything else is just using the bog standard 8250 support.
  10 *
  11 * We follow code flow of serial8250_default_handle_irq() but add
  12 * a check for a break and insert a dummy read on the Rx for the
  13 * immediately following IRQ event.
  14 *
  15 * We re-use the already existing "bug handling" lsr_saved_flags
  16 * field to carry the "what we just did" information from the one
  17 * IRQ event to the next one.
  18 */
  19
  20#include <linux/acpi.h>
  21#include <linux/serial_reg.h>
  22#include <linux/serial_8250.h>
  23
  24#include "8250.h"
  25
  26struct fsl8250_data {
  27        int     line;
  28};
  29
  30int fsl8250_handle_irq(struct uart_port *port)
  31{
  32        unsigned char lsr, orig_lsr;
  33        unsigned long flags;
  34        unsigned int iir;
  35        struct uart_8250_port *up = up_to_u8250p(port);
  36
  37        spin_lock_irqsave(&up->port.lock, flags);
  38
  39        iir = port->serial_in(port, UART_IIR);
  40        if (iir & UART_IIR_NO_INT) {
  41                spin_unlock(&up->port.lock);
  42                return 0;
  43        }
  44
  45        /* This is the WAR; if last event was BRK, then read and return */
  46        if (unlikely(up->lsr_saved_flags & UART_LSR_BI)) {
  47                up->lsr_saved_flags &= ~UART_LSR_BI;
  48                port->serial_in(port, UART_RX);
  49                spin_unlock(&up->port.lock);
  50                return 1;
  51        }
  52
  53        lsr = orig_lsr = up->port.serial_in(&up->port, UART_LSR);
  54
  55        /* Process incoming characters first */
  56        if ((lsr & (UART_LSR_DR | UART_LSR_BI)) &&
  57            (up->ier & (UART_IER_RLSI | UART_IER_RDI))) {
  58                lsr = serial8250_rx_chars(up, lsr);
  59        }
  60
  61        /* Stop processing interrupts on input overrun */
  62        if ((orig_lsr & UART_LSR_OE) && (up->overrun_backoff_time_ms > 0)) {
  63                unsigned long delay;
  64
  65                up->ier = port->serial_in(port, UART_IER);
  66                if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) {
  67                        port->ops->stop_rx(port);
  68                } else {
  69                        /* Keep restarting the timer until
  70                         * the input overrun subsides.
  71                         */
  72                        cancel_delayed_work(&up->overrun_backoff);
  73                }
  74
  75                delay = msecs_to_jiffies(up->overrun_backoff_time_ms);
  76                schedule_delayed_work(&up->overrun_backoff, delay);
  77        }
  78
  79        serial8250_modem_status(up);
  80
  81        if ((lsr & UART_LSR_THRE) && (up->ier & UART_IER_THRI))
  82                serial8250_tx_chars(up);
  83
  84        up->lsr_saved_flags = orig_lsr;
  85
  86        uart_unlock_and_check_sysrq_irqrestore(&up->port, flags);
  87
  88        return 1;
  89}
  90EXPORT_SYMBOL_GPL(fsl8250_handle_irq);
  91
  92#ifdef CONFIG_ACPI
  93static int fsl8250_acpi_probe(struct platform_device *pdev)
  94{
  95        struct fsl8250_data *data;
  96        struct uart_8250_port port8250;
  97        struct device *dev = &pdev->dev;
  98        struct resource *regs;
  99
 100        int ret, irq;
 101
 102        regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 103        if (!regs) {
 104                dev_err(dev, "no registers defined\n");
 105                return -EINVAL;
 106        }
 107
 108        irq = platform_get_irq(pdev, 0);
 109        if (irq < 0)
 110                return irq;
 111
 112        memset(&port8250, 0, sizeof(port8250));
 113
 114        ret = device_property_read_u32(dev, "clock-frequency",
 115                                        &port8250.port.uartclk);
 116        if (ret)
 117                return ret;
 118
 119        spin_lock_init(&port8250.port.lock);
 120
 121        port8250.port.mapbase           = regs->start;
 122        port8250.port.irq               = irq;
 123        port8250.port.handle_irq        = fsl8250_handle_irq;
 124        port8250.port.type              = PORT_16550A;
 125        port8250.port.flags             = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF
 126                                                | UPF_FIXED_PORT | UPF_IOREMAP
 127                                                | UPF_FIXED_TYPE;
 128        port8250.port.dev               = dev;
 129        port8250.port.mapsize           = resource_size(regs);
 130        port8250.port.iotype            = UPIO_MEM;
 131        port8250.port.irqflags          = IRQF_SHARED;
 132
 133        port8250.port.membase = devm_ioremap(dev,  port8250.port.mapbase,
 134                                                        port8250.port.mapsize);
 135        if (!port8250.port.membase)
 136                return -ENOMEM;
 137
 138        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
 139        if (!data)
 140                return -ENOMEM;
 141
 142        data->line = serial8250_register_8250_port(&port8250);
 143        if (data->line < 0)
 144                return data->line;
 145
 146        platform_set_drvdata(pdev, data);
 147        return 0;
 148}
 149
 150static int fsl8250_acpi_remove(struct platform_device *pdev)
 151{
 152        struct fsl8250_data *data = platform_get_drvdata(pdev);
 153
 154        serial8250_unregister_port(data->line);
 155        return 0;
 156}
 157
 158static const struct acpi_device_id fsl_8250_acpi_id[] = {
 159        { "NXP0018", 0 },
 160        { },
 161};
 162MODULE_DEVICE_TABLE(acpi, fsl_8250_acpi_id);
 163
 164static struct platform_driver fsl8250_platform_driver = {
 165        .driver = {
 166                .name                   = "fsl-16550-uart",
 167                .acpi_match_table       = ACPI_PTR(fsl_8250_acpi_id),
 168        },
 169        .probe                  = fsl8250_acpi_probe,
 170        .remove                 = fsl8250_acpi_remove,
 171};
 172
 173module_platform_driver(fsl8250_platform_driver);
 174#endif
 175