[PATCH] serial: 8250: Integrate Fintek into 8250_base

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The 8250_fintek driver advertises as the PNP0501 driver; however this
conflicts with the standard 16550A uart PNP0501. The conflict causes
the 8250_fintek driver to load with _every_ PNP0501, but never probe,
and causing the entire 8250 driver stack to unload if the 8250_fintek
driver is unloaded (modprobe doesn't know that 8250_pnp rather than
8250_fintek claimed the resource).

This patch merges the Fintek driver into 8250_base. On autoconfig_16550
the device is probed to verify if it is a FINTEK device or not. Only the
address ranges that belong to the ACPI device PNP0C02 are probed. If the
vendor and product matches, the port_type is changed to PORT_F81216A.

This custom probing can be disabled completely via configuration. When a
Fintek device is not probed it will behave as a standard 16550A device,
with no RS485 capabilities.

Reported-by: Peter Hurley <peter@xxxxxxxxxxxxxxxxxx>
Signed-off-by: Ricardo Ribalda Delgado <ricardo.ribalda@xxxxxxxxx>
---

This patches includes the comments from Peter for patch:

serial: 8250: Integrate Fintek into 8250_pnp


 drivers/tty/serial/8250/8250.h        |   6 ++
 drivers/tty/serial/8250/8250_fintek.c | 190 ++++++++++++++--------------------
 drivers/tty/serial/8250/8250_port.c   |  14 +++
 drivers/tty/serial/8250/Kconfig       |  22 ++--
 drivers/tty/serial/8250/Makefile      |   2 +-
 include/uapi/linux/serial.h           |   1 +
 6 files changed, 116 insertions(+), 119 deletions(-)

diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index 047a7ba6796a..52b5700a6c59 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -151,6 +151,12 @@ static inline int serial8250_pnp_init(void) { return 0; }
 static inline void serial8250_pnp_exit(void) { }
 #endif
 
+#ifdef CONFIG_SERIAL_8250_FINTEK
+int fintek_8250_probe(struct uart_8250_port *uart);
+#else
+static inline int fintek_8250_probe(struct uart_8250_port *uart) { return 0; }
+#endif
+
 #ifdef CONFIG_ARCH_OMAP1
 static inline int is_omap1_8250(struct uart_8250_port *pt)
 {
diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c
index 89474399ab89..2096d87b6de9 100644
--- a/drivers/tty/serial/8250/8250_fintek.c
+++ b/drivers/tty/serial/8250/8250_fintek.c
@@ -1,9 +1,7 @@
 /*
  *  Probe for F81216A LPC to 4 UART
  *
- *  Based on drivers/tty/serial/8250_pnp.c, by Russell King, et al
- *
- *  Copyright (C) 2014 Ricardo Ribalda, Qtechnology A/S
+ *  Copyright (C) 2014-2016 Ricardo Ribalda, Qtechnology A/S
  *
  *
  * This program is free software; you can redistribute it and/or modify
@@ -15,6 +13,7 @@
 #include <linux/pnp.h>
 #include <linux/kernel.h>
 #include <linux/serial_core.h>
+#include <linux/sfi_acpi.h>
 #include  "8250.h"
 
 #define ADDR_PORT 0
@@ -38,30 +37,44 @@
 #define RXW4C_IRA BIT(3)
 #define TXW4C_IRA BIT(2)
 
-#define DRIVER_NAME "8250_fintek"
-
 struct fintek_8250 {
 	u16 base_port;
 	u8 index;
 	u8 key;
-	long line;
 };
 
+struct fintek_8250_probe {
+	struct fintek_8250 pdata;
+	u16 io_address;
+};
+
+static void _fintek_8250_enter_key(u16 base_port, u8 key)
+{
+
+	outb(key, base_port + ADDR_PORT);
+	outb(key, base_port + ADDR_PORT);
+}
+
 static int fintek_8250_enter_key(u16 base_port, u8 key)
 {
 
-	if (!request_muxed_region(base_port, 2, DRIVER_NAME))
+	if (!request_muxed_region(base_port, 2, "8250_fintek"))
 		return -EBUSY;
 
-	outb(key, base_port + ADDR_PORT);
-	outb(key, base_port + ADDR_PORT);
+	_fintek_8250_enter_key(base_port, key);
+
 	return 0;
 }
 
+static void _fintek_8250_exit_key(u16 base_port)
+{
+	outb(EXIT_KEY, base_port + ADDR_PORT);
+}
+
 static void fintek_8250_exit_key(u16 base_port)
 {
 
-	outb(EXIT_KEY, base_port + ADDR_PORT);
+	return _fintek_8250_exit_key(base_port);
 	release_region(base_port + ADDR_PORT, 2);
 }
 
@@ -138,21 +151,40 @@ static int fintek_8250_rs485_config(struct uart_port *port,
 	return 0;
 }
 
-static int fintek_8250_base_port(u16 io_address, u8 *key, u8 *index)
+static acpi_status check_fintek_resource(struct acpi_resource *res, void *data)
 {
 	static const u16 addr[] = {0x4e, 0x2e};
 	static const u8 keys[] = {0x77, 0xa0, 0x87, 0x67};
+	struct fintek_8250_probe *probe_data = data;
 	int i, j, k;
 
+	if (res->type != ACPI_RESOURCE_TYPE_IO || res->length < 2)
+		return AE_OK;
+
+
 	for (i = 0; i < ARRAY_SIZE(addr); i++) {
+		struct resource mem =
+			DEFINE_RES_IO(res->data.io.minimum, 2);
+
+		if (addr[i] != res->data.io.minimum)
+			continue;
+
+		/*
+		 * Avoid probing on ioports requested by other
+		 * devices.
+		 * request_muxed_region() cannot be used here
+		 * because this function is called from a non
+		 * sleeping context.
+		 */
+		if (!request_resource(&ioport_resource, &mem))
+			continue;
+
 		for (j = 0; j < ARRAY_SIZE(keys); j++) {
 
-			if (fintek_8250_enter_key(addr[i], keys[j]))
-				continue;
-			if (fintek_8250_check_id(addr[i])) {
-				fintek_8250_exit_key(addr[i]);
+			_fintek_8250_enter_key(addr[i], keys[j]);
+
+			if (fintek_8250_check_id(addr[i]))
 				continue;
-			}
 
 			for (k = 0; k < 4; k++) {
 				u16 aux;
@@ -164,119 +196,57 @@ static int fintek_8250_base_port(u16 io_address, u8 *key, u8 *index)
 				aux = inb(addr[i] + DATA_PORT);
 				outb(IO_ADDR2, addr[i] + ADDR_PORT);
 				aux |= inb(addr[i] + DATA_PORT) << 8;
-				if (aux != io_address)
+				if (aux != probe_data->io_address)
 					continue;
 
-				fintek_8250_exit_key(addr[i]);
-				*key = keys[j];
-				*index = k;
-				return addr[i];
+				_fintek_8250_exit_key(addr[i]);
+				probe_data->pdata.key = keys[j];
+				probe_data->pdata.base_port = addr[i];
+				probe_data->pdata.index = k;
+
+				return AE_CTRL_TERMINATE;
 			}
-			fintek_8250_exit_key(addr[i]);
+			_fintek_8250_exit_key(addr[i]);
 		}
 	}
 
-	return -ENODEV;
+	return AE_OK;
+
 }
 
-static int
-fintek_8250_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
+static acpi_status find_fintek_resource(acpi_handle handle, u32 lvl,
+					void *context, void **rv)
 {
-	struct uart_8250_port uart;
-	struct fintek_8250 *pdata;
-	int base_port;
-	u8 key;
-	u8 index;
+	struct fintek_8250_probe *probe_data = context;
 
-	if (!pnp_port_valid(dev, 0))
-		return -ENODEV;
-
-	base_port = fintek_8250_base_port(pnp_port_start(dev, 0), &key, &index);
-	if (base_port < 0)
-		return -ENODEV;
-
-	memset(&uart, 0, sizeof(uart));
-
-	pdata = devm_kzalloc(&dev->dev, sizeof(*pdata), GFP_KERNEL);
-	if (!pdata)
-		return -ENOMEM;
-	uart.port.private_data = pdata;
+	acpi_walk_resources(handle, METHOD_NAME__CRS,
+			    check_fintek_resource, probe_data);
 
-	if (!pnp_irq_valid(dev, 0))
-		return -ENODEV;
-	uart.port.irq = pnp_irq(dev, 0);
-	uart.port.iobase = pnp_port_start(dev, 0);
-	uart.port.iotype = UPIO_PORT;
-	uart.port.rs485_config = fintek_8250_rs485_config;
-
-	uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
-	if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE)
-		uart.port.flags |= UPF_SHARE_IRQ;
-	uart.port.uartclk = 1843200;
-	uart.port.dev = &dev->dev;
-
-	pdata->key = key;
-	pdata->base_port = base_port;
-	pdata->index = index;
-	pdata->line = serial8250_register_8250_port(&uart);
-	if (pdata->line < 0)
-		return -ENODEV;
+	if (probe_data->pdata.base_port)
+		return AE_CTRL_TERMINATE;
 
-	pnp_set_drvdata(dev, pdata);
-	return 0;
+	return AE_OK;
 }
 
-static void fintek_8250_remove(struct pnp_dev *dev)
-{
-	struct fintek_8250 *pdata = pnp_get_drvdata(dev);
 
-	if (pdata)
-		serial8250_unregister_port(pdata->line);
-}
-
-#ifdef CONFIG_PM
-static int fintek_8250_suspend(struct pnp_dev *dev, pm_message_t state)
+int fintek_8250_probe(struct uart_8250_port *uart)
 {
-	struct fintek_8250 *pdata = pnp_get_drvdata(dev);
+	struct fintek_8250 *pdata;
+	struct fintek_8250_probe probe_data;
 
-	if (!pdata)
+	probe_data.io_address = uart->port.iobase;
+	probe_data.pdata.base_port = 0;
+	acpi_get_devices("PNP0C02", find_fintek_resource, &probe_data, NULL);
+	if  (!probe_data.pdata.base_port)
 		return -ENODEV;
-	serial8250_suspend_port(pdata->line);
-	return 0;
-}
-
-static int fintek_8250_resume(struct pnp_dev *dev)
-{
-	struct fintek_8250 *pdata = pnp_get_drvdata(dev);
 
+	pdata = devm_kzalloc(uart->port.dev, sizeof(*pdata), GFP_ATOMIC);
 	if (!pdata)
-		return -ENODEV;
-	serial8250_resume_port(pdata->line);
-	return 0;
-}
-#else
-#define fintek_8250_suspend NULL
-#define fintek_8250_resume NULL
-#endif /* CONFIG_PM */
-
-static const struct pnp_device_id fintek_dev_table[] = {
-	/* Qtechnology Panel PC / IO1000 */
-	{ "PNP0501"},
-	{}
-};
+		return -ENOMEM;
 
-MODULE_DEVICE_TABLE(pnp, fintek_dev_table);
+	memcpy(pdata, &probe_data.pdata, sizeof(probe_data.pdata));
+	uart->port.rs485_config = fintek_8250_rs485_config;
+	uart->port.private_data = pdata;
 
-static struct pnp_driver fintek_8250_driver = {
-	.name		= DRIVER_NAME,
-	.probe		= fintek_8250_probe,
-	.remove		= fintek_8250_remove,
-	.suspend	= fintek_8250_suspend,
-	.resume		= fintek_8250_resume,
-	.id_table	= fintek_dev_table,
-};
-
-module_pnp_driver(fintek_8250_driver);
-MODULE_DESCRIPTION("Fintek F812164 module");
-MODULE_AUTHOR("Ricardo Ribalda <ricardo.ribalda@xxxxxxxxx>");
-MODULE_LICENSE("GPL");
+	return 0;
+}
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index e213da01a3d7..f29b52cead71 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -272,6 +272,14 @@ static const struct serial8250_config uart_config[] = {
 		.rxtrig_bytes	= {1, 4, 8, 14},
 		.flags		= UART_CAP_FIFO,
 	},
+	[PORT_F81216A] = {
+		.name		= "Fintek F81216A",
+		.fifo_size	= 16,
+		.tx_loadsz	= 16,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+		.rxtrig_bytes	= {1, 4, 8, 14},
+		.flags		= UART_CAP_FIFO,
+	},
 };
 
 /* Uart divisor latch read */
@@ -1151,6 +1159,12 @@ static void autoconfig_16550a(struct uart_8250_port *up)
 		up->port.type = PORT_U6_16550A;
 		up->capabilities |= UART_CAP_AFE;
 	}
+
+	/*
+	 * Check if the device is a Fintek F81216A
+	 */
+	if (fintek_8250_probe(up) == 0)
+		up->port.type = PORT_F81216A;
 }
 
 /*
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index 64742a086ae3..0b2855e0d9b7 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -57,6 +57,20 @@ config SERIAL_8250_PNP
 	  This builds standard PNP serial support. You may be able to
 	  disable this feature if you only need legacy serial support.
 
+config SERIAL_8250_FINTEK
+	bool "Support for Fintek F81216A LPC to 4 UART RS485 API"
+	depends on SERIAL_8250 && PNPACPI
+	---help---
+	  Selecting this option will add support for the RS485 capabilities
+	  of the Fintek F81216A LPC to 4 UART.
+
+	  If this option is not selected the device will be configured as a
+	  standard 16550A serial port.
+
+	  Probing relies on an ACPI table with one or more PNP0C02 devices.
+
+	  If unsure, say N.
+
 config SERIAL_8250_CONSOLE
 	bool "Console on 8250/16550 and compatible serial port"
 	depends on SERIAL_8250=y
@@ -359,14 +373,6 @@ config SERIAL_8250_OMAP_TTYO_FIXUP
 	  not booting kernel because the serial console remains silent in case
 	  they forgot to update the command line.
 
-config SERIAL_8250_FINTEK
-	tristate "Support for Fintek F81216A LPC to 4 UART"
-	depends on SERIAL_8250 && PNP
-	help
-	  Selecting this option will add support for the Fintek F81216A
-	  LPC to 4 UART. This device has some RS485 functionality not available
-	  through the PNP driver. If unsure, say N.
-
 config SERIAL_8250_LPC18XX
 	tristate "NXP LPC18xx/43xx serial port support"
 	depends on SERIAL_8250 && OF && (ARCH_LPC18XX || COMPILE_TEST)
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index c9a2d6ed87e9..367d403d28d5 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_SERIAL_8250)		+= 8250.o 8250_base.o
 8250-$(CONFIG_SERIAL_8250_PNP)		+= 8250_pnp.o
 8250_base-y				:= 8250_port.o
 8250_base-$(CONFIG_SERIAL_8250_DMA)	+= 8250_dma.o
+8250_base-$(CONFIG_SERIAL_8250_FINTEK)	+= 8250_fintek.o
 obj-$(CONFIG_SERIAL_8250_GSC)		+= 8250_gsc.o
 obj-$(CONFIG_SERIAL_8250_PCI)		+= 8250_pci.o
 obj-$(CONFIG_SERIAL_8250_HP300)		+= 8250_hp300.o
@@ -23,7 +24,6 @@ obj-$(CONFIG_SERIAL_8250_FSL)		+= 8250_fsl.o
 obj-$(CONFIG_SERIAL_8250_DW)		+= 8250_dw.o
 obj-$(CONFIG_SERIAL_8250_EM)		+= 8250_em.o
 obj-$(CONFIG_SERIAL_8250_OMAP)		+= 8250_omap.o
-obj-$(CONFIG_SERIAL_8250_FINTEK)	+= 8250_fintek.o
 obj-$(CONFIG_SERIAL_8250_LPC18XX)	+= 8250_lpc18xx.o
 obj-$(CONFIG_SERIAL_8250_MT6577)	+= 8250_mtk.o
 obj-$(CONFIG_SERIAL_8250_UNIPHIER)	+= 8250_uniphier.o
diff --git a/include/uapi/linux/serial.h b/include/uapi/linux/serial.h
index 5d59c3ebf459..cf76df415d7d 100644
--- a/include/uapi/linux/serial.h
+++ b/include/uapi/linux/serial.h
@@ -61,6 +61,7 @@ struct serial_struct {
 #define PORT_16850	12
 #define PORT_RSA	13	/* RSA-DV II/S card */
 #define PORT_MAX	13
+#define PORT_F81216A	14
 
 #define SERIAL_IO_PORT	0
 #define SERIAL_IO_HUB6	1
-- 
2.8.0.rc3

--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux