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_irqrestore(&up->port.lock, flags);
  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_irqrestore(&up->port.lock, flags);
  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        uart_unlock_and_check_sysrq(&up->port, flags);
  86        return 1;
  87}
  88EXPORT_SYMBOL_GPL(fsl8250_handle_irq);
  89
  90#ifdef CONFIG_ACPI
  91static int fsl8250_acpi_probe(struct platform_device *pdev)
  92{
  93        struct fsl8250_data *data;
  94        struct uart_8250_port port8250;
  95        struct device *dev = &pdev->dev;
  96        struct resource *regs;
  97
  98        int ret, irq;
  99
 100        regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 101        if (!regs) {
 102                dev_err(dev, "no registers defined\n");
 103                return -EINVAL;
 104        }
 105
 106        irq = platform_get_irq(pdev, 0);
 107        if (irq < 0) {
 108                if (irq != -EPROBE_DEFER)
 109                        dev_err(dev, "cannot get irq\n");
 110                return irq;
 111        }
 112
 113        memset(&port8250, 0, sizeof(port8250));
 114
 115        ret = device_property_read_u32(dev, "clock-frequency",
 116                                        &port8250.port.uartclk);
 117        if (ret)
 118                return ret;
 119
 120        spin_lock_init(&port8250.port.lock);
 121
 122        port8250.port.mapbase           = regs->start;
 123        port8250.port.irq               = irq;
 124        port8250.port.handle_irq        = fsl8250_handle_irq;
 125        port8250.port.type              = PORT_16550A;
 126        port8250.port.flags             = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF
 127                                                | UPF_FIXED_PORT | UPF_IOREMAP
 128                                                | UPF_FIXED_TYPE;
 129        port8250.port.dev               = dev;
 130        port8250.port.mapsize           = resource_size(regs);
 131        port8250.port.iotype            = UPIO_MEM;
 132        port8250.port.irqflags          = IRQF_SHARED;
 133
 134        port8250.port.membase = devm_ioremap(dev,  port8250.port.mapbase,
 135                                                        port8250.port.mapsize);
 136        if (!port8250.port.membase)
 137                return -ENOMEM;
 138
 139        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
 140        if (!data)
 141                return -ENOMEM;
 142
 143        data->line = serial8250_register_8250_port(&port8250);
 144        if (data->line < 0)
 145                return data->line;
 146
 147        platform_set_drvdata(pdev, data);
 148        return 0;
 149}
 150
 151static int fsl8250_acpi_remove(struct platform_device *pdev)
 152{
 153        struct fsl8250_data *data = platform_get_drvdata(pdev);
 154
 155        serial8250_unregister_port(data->line);
 156        return 0;
 157}
 158
 159static const struct acpi_device_id fsl_8250_acpi_id[] = {
 160        { "NXP0018", 0 },
 161        { },
 162};
 163MODULE_DEVICE_TABLE(acpi, fsl_8250_acpi_id);
 164
 165static struct platform_driver fsl8250_platform_driver = {
 166        .driver = {
 167                .name                   = "fsl-16550-uart",
 168                .acpi_match_table       = ACPI_PTR(fsl_8250_acpi_id),
 169        },
 170        .probe                  = fsl8250_acpi_probe,
 171        .remove                 = fsl8250_acpi_remove,
 172};
 173
 174module_platform_driver(fsl8250_platform_driver);
 175#endif
 176