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