All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART.
@ 2012-12-03  3:39 Lv Zheng
  2012-12-03  3:39 ` [RFC PATCH 1/3] UART: Add UART subsystem as a bus Lv Zheng
                   ` (10 more replies)
  0 siblings, 11 replies; 71+ messages in thread
From: Lv Zheng @ 2012-12-03  3:39 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox
  Cc: linux-acpi, linux-serial, Lv Zheng

ACPI 5.0 specification introduces enumeration support for SPB buses. This
patch set adds the UART serial bus enumeration support to Linux using such
mechanism.

NOTE: The [PATCH 3/3] is only for the demonstration purpose and should not
      be merged into any of the published Linux source tree.

Lv Zheng (3):
  UART: Add UART subsystem as a bus.
  ACPI / UART: Add ACPI enumeration support for UART bus.
  UART: Add dummy devices to test the enumeration.

 drivers/acpi/Kconfig                 |    7 +
 drivers/acpi/Makefile                |    1 +
 drivers/acpi/acpi_uart.c             |  255 ++++++++++++++++++++
 drivers/acpi/scan.c                  |    1 +
 drivers/tty/serial/8250/8250.c       |   10 +-
 drivers/tty/serial/8250/8250_dummy.c |  128 ++++++++++
 drivers/tty/serial/8250/Kconfig      |   10 +
 drivers/tty/serial/8250/Makefile     |    1 +
 drivers/tty/serial/Makefile          |    2 +-
 drivers/tty/serial/serial_bus.c      |  426 ++++++++++++++++++++++++++++++++++
 include/linux/acpi_uart.h            |   40 ++++
 include/linux/mod_devicetable.h      |    5 +
 include/linux/serial_8250.h          |    2 +
 include/linux/serial_core.h          |   59 +++++
 14 files changed, 944 insertions(+), 3 deletions(-)
 create mode 100644 drivers/acpi/acpi_uart.c
 create mode 100644 drivers/tty/serial/8250/8250_dummy.c
 create mode 100644 drivers/tty/serial/serial_bus.c
 create mode 100644 include/linux/acpi_uart.h

-- 
1.7.10


^ permalink raw reply	[flat|nested] 71+ messages in thread

* [RFC PATCH 1/3] UART: Add UART subsystem as a bus.
  2012-12-03  3:39 [RFC PATCH 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
@ 2012-12-03  3:39 ` Lv Zheng
  2012-12-03 11:46   ` Alan Cox
  2012-12-04 18:54   ` Mika Westerberg
  2012-12-03  3:40 ` [RFC PATCH 2/3] ACPI / UART: Add ACPI enumeration support for UART bus Lv Zheng
                   ` (9 subsequent siblings)
  10 siblings, 2 replies; 71+ messages in thread
From: Lv Zheng @ 2012-12-03  3:39 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox
  Cc: linux-acpi, linux-serial, Lv Zheng

Tranditional UARTs are used as communication pipes between the hosts
while the modern computing systems equipped with HSU (High Speed UARTs)
would connect on-board target devices using the UART ports. The role of
the UART controllers are changed from the communication facility to the
platform bus.

In the recent ACPI 5.0 specification updates, firmwares are provided the
possibilities to enumerate the UART target devices known to the platform
vendors.
Thus there is the needs for enumerating the UART target devices:
1. hotplug uevent
2. serial configuration
Currently, only serial cards on the specific bus (ex. PCMCIA) can be
enumerated and userspace can obtain the hotplug event of the UART target
devices. Linux kernel is lack of an overall enumeration mechanism for
UART target devices.
In order to send uevent, a device need to be a class device or a bus
device. This patch introduces a bus_type subsystem to manage the new
UART target device type for the purpose of the possible future
extensions.
When the UART target devices are created, userspace uevent rules can
pass the creation details to the userspace driver managers
(ex. hciattach). Here is an example of the uevents and exported
attributes of the new type of devices:

Test DSDT (dummy UART host adapter INTF000 and target device INTF001):
  Device (UA00)
  {
      Name (_HID, "INTF000")  // _HID: Hardware ID
      Name (RBUF, ResourceTemplate ()
      {
          Memory32Fixed (ReadWrite,
              0x00000000,         // Address Base
              0x00001000)         // Address Length
      })
      Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
      {
          Return (RBUF)
      }
      Method (_STA, 0, NotSerialized)  // _STA: Status
      {
          Return (0x0F)
      }
      Device (BTH0)
      {
          Name (_HID, "INTF001")  // _HID: Hardware ID
          Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
          {
              Name (UBUF, ResourceTemplate ()
              {
                  UartSerialBus (0x0001C200, DataBitsEight, StopBitsOne,
                      0xC0, LittleEndian, ParityTypeNone, FlowControlHardware,
                      0x0020, 0x0020, "\\_SB.PCI0.UA00",
                      0x00, ResourceConsumer, ,
                      )
              })
              Return (UBUF)
          }
          Method (_STA, 0, NotSerialized)  // _STA: Status
          {
              Return (0x0F)
          }
      }
  }

uevent and environments:
KERNEL[252.443458] add      /bus/uart (bus)
ACTION=add
DEVPATH=/bus/uart
SEQNUM=1142
SUBSYSTEM=bus

KERNEL[268.491709] add      /devices/platform/INTF000:00/INTF001:00 (uart)
ACTION=add
DEVPATH=/devices/platform/INTF000:00/INTF001:00
DEVTYPE=uart_device
MODALIAS=uart:INTF001:00
SEQNUM=1144
SUBSYSTEM=uart

kobject attribute files:
# cat /sys/bus/uart/devices/INTF001:00/modalias
uart:INTF001:00
# cat /sys/bus/uart/devices/INTF001:00/tty_dev
ttyS0
# cat /sys/bus/uart/devices/INTF001:00/tty_attrs
115200 8N0 HW
# cat /sys/bus/uart/devices/INTF001:00/modem_lines
LE:RTS,CTS,

kobject sysfs links:
# ls -l /sys/bus/uart/devices
INTF001:00 -> ../../../devices/platform/INTF000:00/INTF001:00
# ls -l /sys/devices/platform/INTF000:00/INTF001:00
subsystem -> ../../../../bus/uart
host_node -> ../tty/ttyS0
# ls -l /sys/devices/platform/INTF000:00/tty/ttyS0
target_node -> ../../INTF001:00

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/tty/serial/Makefile     |    2 +-
 drivers/tty/serial/serial_bus.c |  409 +++++++++++++++++++++++++++++++++++++++
 include/linux/mod_devicetable.h |    5 +
 include/linux/serial_core.h     |   56 ++++++
 4 files changed, 471 insertions(+), 1 deletion(-)
 create mode 100644 drivers/tty/serial/serial_bus.c

diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 4f694da..4400521 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -2,7 +2,7 @@
 # Makefile for the kernel serial device drivers.
 #
 
-obj-$(CONFIG_SERIAL_CORE) += serial_core.o
+obj-$(CONFIG_SERIAL_CORE) += serial_core.o serial_bus.o
 obj-$(CONFIG_SERIAL_21285) += 21285.o
 
 # These Sparc drivers have to appear before others such as 8250
diff --git a/drivers/tty/serial/serial_bus.c b/drivers/tty/serial/serial_bus.c
new file mode 100644
index 0000000..a6674da
--- /dev/null
+++ b/drivers/tty/serial/serial_bus.c
@@ -0,0 +1,409 @@
+/*
+ * serial_bus.c - UART bus implementation
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+/*
+ * Tranditional UARTs are used as communication pipes between the hosts
+ * while the modern computing systems equipped with HSU (High Speed UARTs)
+ * would connect on-board target devices using the UART ports. The role of
+ * the UART controllers are changed from the communication facility to the
+ * platform bus.
+ *
+ * UART target devices are created in the kernel as struct uart_device.
+ * It is defined for the following purposes:
+ * 1. Sending hotplug notifications to the userspace
+ * 2. Exporting serial configuration parameters to the userspace
+ * 3. Allowing target device based PM to be added easily
+ *
+ */
+
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+
+struct uart_match {
+	dev_t devt;
+};
+
+static int do_uart_tty_find(struct device *dev, void *data)
+{
+	struct uart_match *match = data;
+	return dev->devt == match->devt;
+}
+
+/**
+ * uart_tty_find - find the associated TTY device for the UART target
+ *		   device
+ * @drv: the low level UART driver
+ * @dev: the parent physical device for the TTY devices
+ * @line: the line number of the UART target device
+ *
+ * Return a matching TTY device for the UART target device.
+ */
+struct device *uart_tty_find(struct uart_driver *drv,
+			     struct device *dev, unsigned int line)
+{
+	struct uart_match match;
+
+	match.devt = MKDEV(drv->tty_driver->major,
+			   drv->tty_driver->minor_start + line);
+
+	return device_find_child(dev, &match, do_uart_tty_find);
+}
+EXPORT_SYMBOL_GPL(uart_tty_find);
+
+/**
+ * uart_tty_name - get the associated TTY device name for the UART target
+ *		   device
+ * @drv: the low level UART driver
+ * @line: the line number of the UART target device
+ * @p: pointer to the buffer containing the returned name
+ *
+ * Return a TTY device name for the UART target device.
+ */
+void uart_tty_name(struct uart_driver *driver, int line, char *p)
+{
+	struct tty_driver *drv;
+
+	BUG_ON(!driver || !driver->tty_driver || !p);
+	drv = driver->tty_driver;
+
+	if (drv->flags & TTY_DRIVER_UNNUMBERED_NODE)
+		strcpy(p, drv->name);
+	else
+		sprintf(p, "%s%d", drv->name, line + drv->name_base);
+}
+EXPORT_SYMBOL_GPL(uart_tty_name);
+
+static ssize_t uart_dev_show_name(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct uart_device *udev = to_uart_device(dev);
+	return sprintf(buf, "%s\n", udev->name);
+}
+
+static ssize_t uart_dev_show_modalias(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct uart_device *udev = to_uart_device(dev);
+	return sprintf(buf, "%s%s\n", UART_MODULE_PREFIX, udev->name);
+}
+
+static ssize_t uart_dev_show_tty_dev(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct uart_device *udev = to_uart_device(dev);
+	return sprintf(buf, "%s\n", udev->tty_name);
+}
+
+static ssize_t uart_dev_show_tty_attrs(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct uart_device *udev = to_uart_device(dev);
+	int len = 0;
+
+	/* baud rate */
+	len += sprintf(buf+len, "%d ", udev->baud);
+
+	/* data bits */
+	switch (udev->cflag & CSIZE) {
+	case CS5:
+		len += sprintf(buf+len, "5");
+		break;
+	case CS6:
+		len += sprintf(buf+len, "6");
+		break;
+	case CS7:
+		len += sprintf(buf+len, "7");
+		break;
+	case CS8:
+	default:
+		len += sprintf(buf+len, "8");
+		break;
+	}
+
+	/* parity */
+	if (udev->cflag & PARODD)
+		len += sprintf(buf+len, "O");
+	else if (udev->cflag & PARENB)
+		len += sprintf(buf+len, "E");
+	else
+		len += sprintf(buf+len, "N");
+
+	/* stop bits */
+	len += sprintf(buf+len, "%d", udev->cflag & CSTOPB ? 1 : 0);
+
+	/* HW/SW control */
+	if (udev->cflag & CRTSCTS)
+		len += sprintf(buf+len, " HW");
+	if ((udev->iflag & (IXON|IXOFF|IXANY)) != 0)
+		len += sprintf(buf+len, " SW");
+
+	len += sprintf(buf+len, "\n");
+	return len;
+}
+
+static ssize_t uart_dev_show_modem_lines(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct uart_device *udev = to_uart_device(dev);
+	int len = 0;
+
+	/* endian */
+	if (udev->mctrl & TIOCM_LE)
+		len += sprintf(buf+len, "LE:");
+	else
+		len += sprintf(buf+len, "BE:");
+
+	/* terminal lines */
+	if (udev->mctrl & TIOCM_DTR)
+		len += sprintf(buf+len, "DTR,");
+	if (udev->mctrl & TIOCM_RTS)
+		len += sprintf(buf+len, "RTS,");
+
+	/* modem lines */
+	if (udev->mctrl & TIOCM_CTS)
+		len += sprintf(buf+len, "CTS,");
+	if (udev->mctrl & TIOCM_CAR)
+		len += sprintf(buf+len, "CAR,");
+	if (udev->mctrl & TIOCM_RNG)
+		len += sprintf(buf+len, "RNG,");
+	if (udev->mctrl & TIOCM_DSR)
+		len += sprintf(buf+len, "DSR,");
+
+	len += sprintf(buf+len, "\n");
+	return len;
+}
+
+static DEVICE_ATTR(name, S_IRUGO, uart_dev_show_name, NULL);
+static DEVICE_ATTR(modalias, S_IRUGO, uart_dev_show_modalias, NULL);
+static DEVICE_ATTR(tty_dev, S_IRUGO, uart_dev_show_tty_dev, NULL);
+static DEVICE_ATTR(tty_attrs, S_IRUGO, uart_dev_show_tty_attrs, NULL);
+static DEVICE_ATTR(modem_lines, S_IRUGO, uart_dev_show_modem_lines, NULL);
+
+static struct attribute *uart_dev_attrs[] = {
+	&dev_attr_name.attr,
+	/* coldplug: modprobe $(cat .../modalias) */
+	&dev_attr_modalias.attr,
+	&dev_attr_tty_dev.attr,
+	&dev_attr_tty_attrs.attr,
+	&dev_attr_modem_lines.attr,
+	NULL,
+};
+
+static struct attribute_group uart_dev_attr_group = {
+	.attrs	= uart_dev_attrs,
+};
+
+static const struct attribute_group *uart_dev_attr_groups[] = {
+	&uart_dev_attr_group,
+	NULL,
+};
+
+#ifdef CONFIG_HOTPLUG
+static int uart_device_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct uart_device *udev = to_uart_device(dev);
+
+	if (add_uevent_var(env, "MODALIAS=%s%s",
+			   UART_MODULE_PREFIX, udev->name))
+		return -ENOMEM;
+
+	dev_dbg(dev, "uevent\n");
+	return 0;
+}
+#else
+#define uart_device_uevent	NULL
+#endif
+
+static void uart_device_release(struct device *dev)
+{
+	struct uart_device *udev = to_uart_device(dev);
+
+	put_device(udev->tty);
+	kfree(udev);
+}
+
+struct device_type uart_device_type = {
+	.name		= "uart_device",
+	.groups		= uart_dev_attr_groups,
+	.uevent		= uart_device_uevent,
+	.release	= uart_device_release,
+};
+EXPORT_SYMBOL_GPL(uart_device_type);
+
+/**
+ * uart_register_device - instantiate a UART device
+ * @drv: low level driver structure
+ * @adap: pointer to the UART target device manager
+ * @device: the physical device communicating with the target device
+ * @info: describes one UART target
+ *
+ * Create a UART target device.
+ *
+ * This returns the new UART target device, which may be saved for later use
+ * with uart_unregister_device; or NULL to indicate an error.
+ */
+struct uart_device *uart_register_device(struct uart_driver *drv,
+					 struct klist *adap,
+					 struct device *dev,
+					 struct uart_board_info const *info)
+{
+	struct uart_device *udev;
+	struct device *tty;
+	int status;
+
+	BUG_ON(info->line >= drv->nr);
+	BUG_ON((!adap && !dev) || !drv);
+
+	udev = kzalloc(sizeof(struct uart_device), GFP_KERNEL);
+	if (!udev)
+		return NULL;
+
+	strlcpy(udev->name, info->type, sizeof(udev->name));
+
+	udev->baud = info->baud;
+	udev->cflag = info->cflag;
+	udev->iflag = info->iflag;
+	udev->mctrl = info->mctrl;
+
+	udev->dev.parent = dev;
+	udev->dev.bus = &uart_bus_type;
+	udev->dev.type = &uart_device_type;
+
+	tty = uart_tty_find(drv, dev, info->line);
+	if (!tty) {
+		dev_err(&udev->dev, "Cannot find associated tty device.\n");
+		goto fail;
+	}
+	udev->tty = get_device(tty);
+	uart_tty_name(drv, info->line, udev->tty_name);
+
+	dev_set_name(&udev->dev, "%s", udev->name);
+	status = device_register(&udev->dev);
+	if (status) {
+		dev_err(&udev->dev, "Failed to register uart device.\n");
+		goto fail2;
+	}
+
+	status = sysfs_create_link(&tty->kobj, &udev->dev.kobj, "target_node");
+	status = sysfs_create_link(&udev->dev.kobj, &tty->kobj, "host_node");
+
+	if (adap) {
+		udev->adap = adap;
+		klist_add_tail(&udev->klist_parent, adap);
+	}
+
+	return udev;
+
+fail2:
+	put_device(tty);
+fail:
+	kfree(udev);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(uart_register_device);
+
+/**
+ * uart_unregister_device - unregister a UART device
+ * @device: value returned from uart_register_device()
+ *
+ * Reverse effect of uart_register_device().
+ */
+void uart_unregister_device(struct uart_device *udev)
+{
+	if (udev->adap)
+		klist_del(&udev->klist_parent);
+	sysfs_remove_link(&udev->dev.kobj, "host_node");
+	if (udev->tty)
+		sysfs_remove_link(&udev->tty->kobj, "target_node");
+	device_unregister(&udev->dev);
+}
+EXPORT_SYMBOL_GPL(uart_unreigster_device);
+
+static void klist_uart_get(struct klist_node *n)
+{
+	struct uart_device *udev = uart_device_from_parent(n);
+	get_device(&udev->dev);
+}
+
+static void klist_uart_put(struct klist_node *n)
+{
+	struct uart_device *udev = uart_device_from_parent(n);
+	put_device(&udev->dev);
+}
+
+static struct uart_device *uart_next_device(struct klist_iter *i)
+{
+	struct klist_node *n = klist_next(i);
+	return n ? uart_device_from_parent(n) : NULL;
+}
+
+/**
+ * uart_add_adapter - register a UART adapter
+ * @adap: pointer to the device manager
+ *
+ * Initialize a UART target device manager - adapter.
+ */
+int uart_add_adapter(struct klist *adap)
+{
+	klist_init(adap, klist_uart_get, klist_uart_put);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(uart_add_adapter);
+
+/**
+ * uart_del_adapter - unregister a UART adapter
+ * @adap: pointer to the device manager
+ *
+ * Reverse effect of uart_add_adapter().
+ */
+void uart_del_adapter(struct klist *adap)
+{
+	struct klist_iter i;
+	struct uart_device *udev;
+
+	klist_iter_init(adap, &i);
+	while ((udev = uart_next_device(&i)))
+		uart_unregister_device(udev);
+	klist_iter_exit(&i);
+}
+EXPORT_SYMBOL_GPL(uart_del_adapter);
+
+struct bus_type uart_bus_type = {
+	.name		= "uart",
+};
+EXPORT_SYMBOL_GPL(uart_bus_type);
+
+static int __init uart_bus_init(void)
+{
+	int retval;
+
+	retval = bus_register(&uart_bus_type);
+	if (retval)
+		return retval;
+
+	return 0;
+}
+
+static void __exit uart_bus_exit(void)
+{
+	bus_unregister(&uart_bus_type);
+}
+
+subsys_initcall(uart_bus_init);
+module_exit(uart_bus_exit);
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index fed3def..28df140 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -455,6 +455,11 @@ struct spi_device_id {
 			__attribute__((aligned(sizeof(kernel_ulong_t))));
 };
 
+/* uart */
+
+#define UART_NAME_SIZE	32
+#define UART_MODULE_PREFIX "uart:"
+
 /* dmi */
 enum dmi_field {
 	DMI_NONE,
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 3c43022..422f8cc4 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -31,10 +31,13 @@
 #include <linux/sysrq.h>
 #include <linux/pps_kernel.h>
 #include <uapi/linux/serial_core.h>
+#include <linux/mod_devicetable.h>
 
 struct uart_port;
+struct uart_device;
 struct serial_struct;
 struct device;
+struct uart_board_info;
 
 /*
  * This structure describes all the operations that can be
@@ -367,4 +370,57 @@ static inline int uart_handle_break(struct uart_port *port)
 					 (cflag) & CRTSCTS || \
 					 !((cflag) & CLOCAL))
 
+/*
+ * UART Bus
+ */
+struct uart_board_info {
+	char type[UART_NAME_SIZE];
+	unsigned int line;	/* port index */
+	unsigned int cflag;	/* termio cflag */
+	unsigned int iflag;	/* termio iflag */
+	unsigned int mctrl;	/* modem ctrl settings */
+	unsigned int baud;
+	int irq;
+};
+
+extern struct bus_type uart_bus_type;
+
+int uart_add_adapter(struct klist *adap);
+void uart_del_adapter(struct klist *adap);
+
+struct uart_device {
+	char			name[UART_NAME_SIZE];
+	char			tty_name[64];
+	unsigned int		cflag;	/* termio cflag */
+	unsigned int		iflag;	/* termio iflag */
+	unsigned int		mctrl;	/* modem ctrl settings */
+	unsigned int		baud;
+	struct device		dev;
+	struct device		*tty;
+	struct klist_node	klist_parent;
+	struct klist		*adap;	/* set for multi-port adapter */
+};
+
+extern struct device_type uart_device_type;
+
+#define is_uart_device(d) ((d) && (d)->type == &uart_device_type)
+#define to_uart_device(d) container_of(d, struct uart_device, dev)
+#define uart_device_from_parent(n)	\
+	container_of(n, struct uart_device, klist_parent)
+
+static inline struct uart_device *uart_verify_device(struct device *dev)
+{
+	return is_uart_device(dev) ? to_uart_device(dev) : NULL;
+}
+
+struct uart_device *uart_register_device(struct uart_driver *drv,
+					 struct klist *adap,
+					 struct device *dev,
+					 struct uart_board_info const *info);
+void uart_unregister_device(struct uart_device *udev);
+
+struct device *uart_tty_find(struct uart_driver *drv,
+			     struct device *dev, unsigned int line);
+void uart_tty_name(struct uart_driver *driver, int index, char *p);
+
 #endif /* LINUX_SERIAL_CORE_H */
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [RFC PATCH 2/3] ACPI / UART: Add ACPI enumeration support for UART bus.
  2012-12-03  3:39 [RFC PATCH 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
  2012-12-03  3:39 ` [RFC PATCH 1/3] UART: Add UART subsystem as a bus Lv Zheng
@ 2012-12-03  3:40 ` Lv Zheng
  2012-12-03  3:40 ` [RFC PATCH 3/3] UART: Add dummy devices to test the enumeration Lv Zheng
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 71+ messages in thread
From: Lv Zheng @ 2012-12-03  3:40 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox
  Cc: linux-acpi, linux-serial, Lv Zheng

ACPI 5.0 specification introduces the methods of enumerating the target
devices connected on the serial buses.
This patch follows the specification, implementing such UART enumeration
machanism for the Linux.

In order to use this UART device enumeration mechanism, driver writers
are required to call the following APIs:
1. APIs called _after_ the creation of the uart ports:
   adap = acpi_uart_register_devices(driver, adap, parent, line);
   Where:
    driver: the low level UART driver
    parent: the physical device of the UART ports
    adap: the management list of the target devices, can be set as NULL
    line: the line number of the target device
2. APIs called _before_ the deletion of the uart ports:
   acpi_uart_unregister_devices(adap);
   Where:
    adap: the UART target device manager

NOTE: If the driver writer has already created the management list for
      the UART target devices, the adap parameter can be set to the
      already created non-NULL value.
NOTE: The ACPI 5.0 specification assumes one physical device per-port.
      In this situation, the line parameter might be set to 0 when the
      acpi_uart_register_devices() is called.
      This patch set can also support the multi-port UART adapters,
      where the line should be set as ACPI_UART_LINE_UNKNOWN.

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/acpi/Kconfig      |    7 ++
 drivers/acpi/Makefile     |    1 +
 drivers/acpi/acpi_uart.c  |  255 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/acpi_uart.h |   40 +++++++
 4 files changed, 303 insertions(+)
 create mode 100644 drivers/acpi/acpi_uart.c
 create mode 100644 include/linux/acpi_uart.h

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 0300bf6..d40203f 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -187,6 +187,12 @@ config ACPI_I2C
 	help
 	  ACPI I2C enumeration support.
 
+config ACPI_UART
+	def_tristate SERIAL_CORE
+	depends on SERIAL_CORE
+	help
+	  ACPI UART enumeration support.
+
 config ACPI_PROCESSOR
 	tristate "Processor"
 	select THERMAL
@@ -200,6 +206,7 @@ config ACPI_PROCESSOR
 
 	  To compile this driver as a module, choose M here:
 	  the module will be called processor.
+
 config ACPI_IPMI
 	tristate "IPMI"
 	depends on EXPERIMENTAL && IPMI_SI && IPMI_HANDLER
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 2a4502b..784f332 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -71,6 +71,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS)	+= ec_sys.o
 obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
 obj-$(CONFIG_ACPI_BGRT)		+= bgrt.o
 obj-$(CONFIG_ACPI_I2C)		+= acpi_i2c.o
+obj-$(CONFIG_ACPI_UART)		+= acpi_uart.o
 
 # processor has its own "processor." module_param namespace
 processor-y			:= processor_driver.o processor_throttling.o
diff --git a/drivers/acpi/acpi_uart.c b/drivers/acpi/acpi_uart.c
new file mode 100644
index 0000000..82bbfd4
--- /dev/null
+++ b/drivers/acpi/acpi_uart.c
@@ -0,0 +1,255 @@
+/*
+ * acpi_uart.c - ACPI UART enumeration support
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+#include <linux/init.h>
+#include <linux/serial_core.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <acpi/acpi.h>
+#include <acpi/acpi_bus.h>
+
+
+static int
+acpi_uart_add_resources(struct acpi_resource *ares, void *context)
+{
+	struct uart_board_info *info = context;
+
+	if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
+		struct acpi_resource_uart_serialbus *sb;
+
+		sb = &ares->data.uart_serial_bus;
+		if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_UART)
+			return 1;
+
+		/* baud rate */
+		info->baud = sb->default_baud_rate;
+
+		/* data bits */
+		info->cflag &= ~CSIZE;
+		switch (sb->data_bits) {
+		case ACPI_UART_5_DATA_BITS:
+			info->cflag |= CS5;
+			break;
+		case ACPI_UART_6_DATA_BITS:
+			info->cflag |= CS6;
+			break;
+		case ACPI_UART_7_DATA_BITS:
+			info->cflag |= CS7;
+			break;
+		case ACPI_UART_8_DATA_BITS:
+		default:
+			info->cflag |= CS8;
+			break;
+		}
+
+		/* parity */
+		info->cflag &= ~(PARENB | PARODD);
+		if (sb->parity == ACPI_UART_PARITY_EVEN)
+			info->cflag |= PARENB;
+		else if (sb->parity == ACPI_UART_PARITY_ODD)
+			info->cflag |= (PARENB | PARODD);
+
+		/* stop bits */
+		if (sb->stop_bits == ACPI_UART_2_STOP_BITS)
+			info->cflag |= CSTOPB;
+		else
+			info->cflag &= ~CSTOPB;
+
+		/* HW control */
+		if (sb->flow_control & ACPI_UART_FLOW_CONTROL_HW)
+			info->cflag |= CRTSCTS;
+		else
+			info->cflag &= ~CRTSCTS;
+
+		/* SW control */
+		if (sb->flow_control & ACPI_UART_FLOW_CONTROL_XON_XOFF)
+			info->iflag |= (IXON | IXOFF);
+		else
+			info->iflag &= ~(IXON|IXOFF|IXANY);
+
+		/* endianess */
+		if (sb->endian == ACPI_UART_LITTLE_ENDIAN)
+			info->mctrl |= TIOCM_LE;
+		else
+			info->mctrl &= ~TIOCM_LE;
+
+		/* terminal lines */
+		if (sb->lines_enabled & ACPI_UART_DATA_TERMINAL_READY)
+			info->mctrl |= TIOCM_DTR;
+		else
+			info->mctrl &= ~TIOCM_DTR;
+		if (sb->lines_enabled & ACPI_UART_REQUEST_TO_SEND)
+			info->mctrl |= TIOCM_RTS;
+		else
+			info->mctrl &= ~TIOCM_RTS;
+
+		/* modem lines */
+		if (sb->lines_enabled & ACPI_UART_CLEAR_TO_SEND)
+			info->mctrl |= TIOCM_CTS;
+		else
+			info->mctrl &= ~TIOCM_CTS;
+		if (sb->lines_enabled & ACPI_UART_CARRIER_DETECT)
+			info->mctrl |= TIOCM_CAR;
+		else
+			info->mctrl &= ~TIOCM_CAR;
+		if (sb->lines_enabled & ACPI_UART_RING_INDICATOR)
+			info->mctrl |= TIOCM_RNG;
+		else
+			info->mctrl &= ~TIOCM_RNG;
+		if (sb->lines_enabled & ACPI_UART_DATA_SET_READY)
+			info->mctrl |= TIOCM_DSR;
+		else
+			info->mctrl &= ~TIOCM_DSR;
+	} else if (info->irq < 0) {
+		struct resource r;
+
+		if (acpi_dev_resource_interrupt(ares, 0, &r))
+			info->irq = r.start;
+	}
+
+	return 1;
+}
+
+struct acpi_uart_walk {
+	struct uart_driver *drv;
+	struct device *parent;
+	unsigned int line;
+};
+
+static acpi_status acpi_uart_add_device(acpi_handle handle, u32 level,
+					void *context, void **return_value)
+{
+	struct acpi_uart_walk *walk = context;
+	struct uart_driver *drv = walk->drv;
+	struct device *parent = walk->parent;
+	unsigned int line = walk->line;
+	struct acpi_device_info *info;
+	struct uart_board_info board_info;
+	struct acpi_device *adev;
+	struct list_head resource_list;
+	int ret;
+	acpi_status status;
+	struct klist *adap = *return_value;
+	struct uart_device *udev;
+
+	BUG_ON(!parent || !adap || !drv);
+
+	if (acpi_bus_get_device(handle, &adev))
+		return AE_OK;
+	if (acpi_bus_get_status(adev) || !adev->status.present)
+		return AE_OK;
+
+	/*
+	 * Current BIOS will create physical adapter DEVICE per port.
+	 * This implementation assumes multi-port DEVICE will use _ADR
+	 * as serial port line number though there is no such explicit
+	 * proof for this assumption in the ACPI specification.
+	 *
+	 * NOTE: There is no real BIOS implementation using _ADR as line
+	 *       number.
+	 */
+	if (line >= drv->nr) {
+		status = acpi_get_object_info(handle, &info);
+		if (ACPI_FAILURE(status))
+			return AE_OK;
+		if (info->valid & ACPI_VALID_ADR)
+			line = info->address;
+		if (line >= drv->nr)
+			return AE_OK;
+	}
+
+	BUG_ON(line >= drv->nr);
+
+	memset(&board_info, 0, sizeof(board_info));
+	board_info.irq = -1;
+	board_info.line = line;
+
+	INIT_LIST_HEAD(&resource_list);
+	ret = acpi_dev_get_resources(adev, &resource_list,
+				     acpi_uart_add_resources, &board_info);
+	acpi_dev_free_resource_list(&resource_list);
+
+	if (ret < 0 || !board_info.baud)
+		return AE_OK;
+
+	strlcpy(board_info.type, dev_name(&adev->dev), sizeof(board_info.type));
+
+	udev = uart_register_device(drv, adap, parent, &board_info);
+	if (!udev) {
+		dev_err(&adev->dev, "failed to add #%d uart device from ACPI\n",
+			line);
+		return AE_OK;
+	}
+
+	return AE_OK;
+}
+
+/**
+ * acpi_uart_register_devices - enumerate the UART slave devices behind the
+ *                              physical UART adapter
+ * @drv: pointer to the low level UART driver
+ * @parent: the physical adapter device containing the port(s)
+ * @line: serial line number
+ *
+ * Enumerate all UART slave devices behind the adapter by walking the ACPI
+ * namespace. When a device is found, it will be added to the Linux device
+ * model and bound to the corresponding ACPI handle.
+ * If the physical device is a multiple port device, line should be -1.
+ * If the physical device is a single port device, line should be the line
+ * number.
+ */
+struct klist *acpi_uart_register_devices(struct uart_driver *drv,
+					 struct klist *adap,
+					 struct device *parent,
+					 unsigned int line)
+{
+	acpi_handle handle;
+	acpi_status status;
+	struct klist *klist = NULL;
+	struct acpi_uart_walk walk = {drv, parent, line};
+
+	BUG_ON(!drv || !parent);
+
+	handle = ACPI_HANDLE(parent);
+	if (!handle)
+		return NULL;
+
+	if (!adap) {
+		klist = kzalloc(sizeof(struct klist), GFP_KERNEL);
+		if (!klist)
+			return NULL;
+		(void)uart_add_adapter(klist);
+		adap = klist;
+	}
+
+	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
+				     acpi_uart_add_device, NULL, &walk,
+				     (void **)&adap);
+	if (ACPI_FAILURE(status)) {
+		dev_warn(parent, "failed to enumerate UART slaves\n");
+		goto fail;
+	}
+
+	return adap;
+
+fail:
+	uart_del_adapter(klist);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(acpi_uart_register_devices);
+
+void acpi_uart_unregister_devices(struct klist *adap)
+{
+	uart_del_adapter(adap);
+	kfree(adap);
+}
+EXPORT_SYMBOL_GPL(acpi_uart_unregister_devices);
diff --git a/include/linux/acpi_uart.h b/include/linux/acpi_uart.h
new file mode 100644
index 0000000..512f047
--- /dev/null
+++ b/include/linux/acpi_uart.h
@@ -0,0 +1,40 @@
+/*
+ * acpi_uart.h - ACPI UART enumeration support
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+#ifndef _LINUX_ACPI_UART_H
+#define _LINUX_ACPI_UART_H
+
+struct uart_driver;
+
+#define ACPI_UART_LINE_UNKNOWN	((unsigned int)-1)
+
+#if IS_ENABLED(CONFIG_ACPI_UART)
+struct klist *acpi_uart_register_devices(struct uart_driver *drv,
+					 struct klist *adap,
+					 struct device *parent,
+					 unsigned int line);
+void acpi_uart_unregister_devices(struct klist *adap);
+#else
+static inline struct klist *acpi_uart_register_devices(struct uart_driver *drv,
+						       struct klist *adap,
+						       struct device *parent,
+						       unsigned int line)
+{
+	return NULL;
+}
+
+static inline void acpi_uart_unregister_devices(struct klist *adap)
+{
+}
+#endif
+
+#endif /* _LINUX_ACPI_UART_H */
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [RFC PATCH 3/3] UART: Add dummy devices to test the enumeration.
  2012-12-03  3:39 [RFC PATCH 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
  2012-12-03  3:39 ` [RFC PATCH 1/3] UART: Add UART subsystem as a bus Lv Zheng
  2012-12-03  3:40 ` [RFC PATCH 2/3] ACPI / UART: Add ACPI enumeration support for UART bus Lv Zheng
@ 2012-12-03  3:40 ` Lv Zheng
  2012-12-05  3:51 ` [PATCH v2 0/4] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 71+ messages in thread
From: Lv Zheng @ 2012-12-03  3:40 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox
  Cc: linux-acpi, linux-serial, Lv Zheng

This is a hidden test patch that would not be sent to the public.
There might be some udev add-ons for matching the UART target devices from
the user space.

1. The result of the UART dummy target device is as follows:

# udevadm monitor --kernel --environment > ~/uart.uevents
# echo add > /sys/bus/uart/uevent
# echo add > /sys/bus/uart/devices/DUMMY/uevent
# cat ~/uart.uevents
monitor will print the received events for:
KERNEL - the kernel uevent

KERNEL[252.443458] add      /bus/uart (bus)
ACTION=add
DEVPATH=/bus/uart
SEQNUM=1142
SUBSYSTEM=bus

KERNEL[268.491709] add      /devices/platform/serial8250/DUMMY (uart)
ACTION=add
DEVPATH=/devices/platform/serial8250/DUMMY
DEVTYPE=uart_device
MODALIAS=uart:DUMMY
SEQNUM=1144
SUBSYSTEM=uart

# cat /sys/bus/uart/devices/DUMMY/modalias
uart:DUMMY
# cat /sys/bus/uart/devices/DUMMY/tty_dev
ttyS0
# cat /sys/bus/uart/devices/DUMMY/tty_attrs
115200 8N1 HW SW
# cat /sys/bus/uart/devices/DUMMY/modem_lines
LE:RTS,

2. The result of the UART customized DSDT target device is as follows:

The test result is attached as part of the first patch in this series.

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/acpi/scan.c                  |    1 +
 drivers/tty/serial/8250/8250.c       |   10 ++-
 drivers/tty/serial/8250/8250_dummy.c |  128 ++++++++++++++++++++++++++++++++++
 drivers/tty/serial/8250/Kconfig      |   10 +++
 drivers/tty/serial/8250/Makefile     |    1 +
 drivers/tty/serial/serial_bus.c      |   17 +++++
 include/linux/serial_8250.h          |    2 +
 include/linux/serial_core.h          |    3 +
 8 files changed, 170 insertions(+), 2 deletions(-)
 create mode 100644 drivers/tty/serial/8250/8250_dummy.c

diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 67a7fa6..4892015 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -36,6 +36,7 @@ static const char *dummy_hid = "device";
 static const struct acpi_device_id acpi_platform_device_ids[] = {
 
 	{ "PNP0D40" },
+	{ "INTF000" },
 
 	{ }
 };
diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c
index 3ba4234..6f503f4 100644
--- a/drivers/tty/serial/8250/8250.c
+++ b/drivers/tty/serial/8250/8250.c
@@ -56,7 +56,7 @@ static unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
 
 static unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS;
 
-static struct uart_driver serial8250_reg;
+struct uart_driver serial8250_reg;
 
 static int serial_index(struct uart_port *port)
 {
@@ -2902,7 +2902,7 @@ int serial8250_find_port(struct uart_port *p)
 #define SERIAL8250_CONSOLE	NULL
 #endif
 
-static struct uart_driver serial8250_reg = {
+struct uart_driver serial8250_reg = {
 	.owner			= THIS_MODULE,
 	.driver_name		= "serial",
 	.dev_name		= "ttyS",
@@ -2910,6 +2910,7 @@ static struct uart_driver serial8250_reg = {
 	.minor			= 64,
 	.cons			= SERIAL8250_CONSOLE,
 };
+EXPORT_SYMBOL_GPL(serial8250_reg);
 
 /*
  * early_serial_setup - early registration for 8250 ports
@@ -2987,6 +2988,8 @@ void serial8250_resume_port(int line)
 	uart_resume_port(&serial8250_reg, port);
 }
 
+struct uart_device *uart_dummy;
+
 /*
  * Register a set of serial devices attached to a platform device.  The
  * list is terminated with a zero flags entry, which means we expect
@@ -3032,6 +3035,8 @@ static int __devinit serial8250_probe(struct platform_device *dev)
 				p->irq, ret);
 		}
 	}
+	uart_dummy = uart_register_dummy(&serial8250_reg, &dev->dev, 0);
+
 	return 0;
 }
 
@@ -3042,6 +3047,7 @@ static int __devexit serial8250_remove(struct platform_device *dev)
 {
 	int i;
 
+	uart_unregister_device(uart_dummy);
 	for (i = 0; i < nr_uarts; i++) {
 		struct uart_8250_port *up = &serial8250_ports[i];
 
diff --git a/drivers/tty/serial/8250/8250_dummy.c b/drivers/tty/serial/8250/8250_dummy.c
new file mode 100644
index 0000000..5720fbb
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_dummy.c
@@ -0,0 +1,128 @@
+/*
+ * 8250_dummy.c: Dummy 8250 UART target device enumerator
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/acpi.h>
+#include <linux/acpi_uart.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct dummy8250_data {
+	int	last_lcr;
+	int	line;
+};
+
+static void dummy8250_serial_out(struct uart_port *p, int offset, int value)
+{
+}
+
+static unsigned int dummy8250_serial_in(struct uart_port *p, int offset)
+{
+	return 0;
+}
+
+struct klist *dummy8250_mgr;
+int dummy8250_num;
+
+static int __devinit dummy8250_probe(struct platform_device *pdev)
+{
+	struct uart_8250_port uart = {};
+	struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	struct dummy8250_data *data;
+#ifdef CONFIG_ACPI_UART
+	struct klist *mgr;
+#endif
+
+	dev_info(&pdev->dev, "1\n");
+	if (!regs) {
+		dev_err(&pdev->dev, "no registers defined\n");
+		return -EINVAL;
+	}
+
+	dev_info(&pdev->dev, "2\n");
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+	uart.port.private_data = data;
+
+	spin_lock_init(&uart.port.lock);
+	uart.port.mapbase = regs->start;
+	uart.port.type = PORT_8250;
+	uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP |
+		UPF_FIXED_PORT | UPF_FIXED_TYPE;
+	uart.port.dev = &pdev->dev;
+
+	uart.port.iotype = UPIO_MEM;
+	uart.port.serial_in = dummy8250_serial_in;
+	uart.port.serial_out = dummy8250_serial_out;
+	uart.port.uartclk = 40000000;
+
+	dev_info(&pdev->dev, "3\n");
+	data->line = serial8250_register_8250_port(&uart);
+	if (data->line < 0)
+		return data->line;
+
+#ifdef CONFIG_ACPI_UART
+	dev_info(&pdev->dev, "4\n");
+	mgr = acpi_uart_register_devices(&serial8250_reg, dummy8250_mgr,
+					 &pdev->dev, data->line);
+	if (mgr) {
+		dummy8250_mgr = mgr;
+		dummy8250_num++;
+	}
+#endif
+	dev_info(&pdev->dev, "5\n");
+	platform_set_drvdata(pdev, data);
+
+	return 0;
+}
+
+static int __devexit dummy8250_remove(struct platform_device *pdev)
+{
+	struct dummy8250_data *data = platform_get_drvdata(pdev);
+
+#ifdef CONFIG_ACPI_UART
+	dummy8250_num--;
+	if (!dummy8250_num)
+		acpi_uart_unregister_devices(dummy8250_mgr);
+#endif
+	serial8250_unregister_port(data->line);
+
+	return 0;
+}
+
+static const struct acpi_device_id dummy8250_match[] = {
+	{ .id = "INTF000" },
+	{ /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(acpi, dummy8250_match);
+
+static struct platform_driver dummy8250_platform_driver = {
+	.driver = {
+		.name			= "dummy-uart",
+		.owner			= THIS_MODULE,
+		.acpi_match_table	= dummy8250_match,
+	},
+	.probe				= dummy8250_probe,
+	.remove				= __devexit_p(dummy8250_remove),
+};
+
+module_platform_driver(dummy8250_platform_driver);
+
+MODULE_AUTHOR("Lv Zheng");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Dummy 8250 serial port driver");
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index f3d283f..3ba480a 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -270,6 +270,16 @@ config SERIAL_8250_DW
 	  Selecting this option will enable handling of the extra features
 	  present in the Synopsys DesignWare APB UART.
 
+config SERIAL_8250_DUMMY
+	tristate "Support for dummy ACPI 8250"
+	depends on SERIAL_8250 && ACPI
+	help
+	  Selecting this option will enable a test UART target device DUMMY
+	  under the ISA serial8250 and a test UART host adapter INTF000
+	  as an platform device for the purpose of testing the ACPI UART
+	  enumeration support.
+	  If unsure, say "N" here.
+
 config SERIAL_8250_EM
 	tristate "Support for Emma Mobile intergrated serial port"
 	depends on SERIAL_8250 && ARM && HAVE_CLK
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index 108fe7f..fb82aa9 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -19,3 +19,4 @@ obj-$(CONFIG_SERIAL_8250_HUB6)		+= 8250_hub6.o
 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_DUMMY)		+= 8250_dummy.o
diff --git a/drivers/tty/serial/serial_bus.c b/drivers/tty/serial/serial_bus.c
index a6674da..e3d0509 100644
--- a/drivers/tty/serial/serial_bus.c
+++ b/drivers/tty/serial/serial_bus.c
@@ -384,6 +384,23 @@ void uart_del_adapter(struct klist *adap)
 }
 EXPORT_SYMBOL_GPL(uart_del_adapter);
 
+static struct uart_board_info dummy_target = {
+	.type = "DUMMY",
+	.baud = 115200,
+	.cflag = CS8 | CSTOPB | CRTSCTS,
+	.iflag = (IXON | IXOFF),
+	.mctrl = TIOCM_LE | TIOCM_RTS,
+};
+
+struct uart_device *uart_register_dummy(struct uart_driver *drv,
+					struct device *dev,
+					unsigned int line)
+{
+	dummy_target.line = line;
+	return uart_register_device(drv, NULL, dev, &dummy_target);
+}
+EXPORT_SYMBOL_GPL(uart_register_dummy);
+
 struct bus_type uart_bus_type = {
 	.name		= "uart",
 };
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index c174c90..3b6c260 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -120,4 +120,6 @@ extern void serial8250_set_isa_configurator(void (*v)
 					(int port, struct uart_port *up,
 						unsigned short *capabilities));
 
+extern struct uart_driver serial8250_reg;
+
 #endif
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 422f8cc4..a3d1d5d 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -418,6 +418,9 @@ struct uart_device *uart_register_device(struct uart_driver *drv,
 					 struct device *dev,
 					 struct uart_board_info const *info);
 void uart_unregister_device(struct uart_device *udev);
+struct uart_device *uart_register_dummy(struct uart_driver *drv,
+					struct device *dev,
+					unsigned int line);
 
 struct device *uart_tty_find(struct uart_driver *drv,
 			     struct device *dev, unsigned int line);
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* Re: [RFC PATCH 1/3] UART: Add UART subsystem as a bus.
  2012-12-03  3:39 ` [RFC PATCH 1/3] UART: Add UART subsystem as a bus Lv Zheng
@ 2012-12-03 11:46   ` Alan Cox
  2012-12-05  3:37     ` Zheng, Lv
  2012-12-04 18:54   ` Mika Westerberg
  1 sibling, 1 reply; 71+ messages in thread
From: Alan Cox @ 2012-12-03 11:46 UTC (permalink / raw)
  To: Lv Zheng
  Cc: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, linux-acpi,
	linux-serial

> + * uart_tty_find - find the associated TTY device for the UART target
> + *		   device
> + * @drv: the low level UART driver
> + * @dev: the parent physical device for the TTY devices
> + * @line: the line number of the UART target device
> + *

Our top level abstraction is struct tty and struct tty_port. There is
nothing requiring a serial tty is using the uart helper layer, nor
should there be, so your code needs to support devices not using the
uart layer. Otherwise it looks quite reasonable.

our 3.7 tty layer has sysfs nodes on the tty which may also help

Alan

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [RFC PATCH 1/3] UART: Add UART subsystem as a bus.
  2012-12-03  3:39 ` [RFC PATCH 1/3] UART: Add UART subsystem as a bus Lv Zheng
  2012-12-03 11:46   ` Alan Cox
@ 2012-12-04 18:54   ` Mika Westerberg
  2012-12-04 19:50     ` Alan Cox
  2012-12-05  3:49     ` Zheng, Lv
  1 sibling, 2 replies; 71+ messages in thread
From: Mika Westerberg @ 2012-12-04 18:54 UTC (permalink / raw)
  To: Lv Zheng
  Cc: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox,
	linux-acpi, linux-serial

On Mon, Dec 03, 2012 at 11:39:59AM +0800, Lv Zheng wrote:
> kobject attribute files:
> # cat /sys/bus/uart/devices/INTF001:00/modalias
> uart:INTF001:00
> # cat /sys/bus/uart/devices/INTF001:00/tty_dev
> ttyS0
> # cat /sys/bus/uart/devices/INTF001:00/tty_attrs
> 115200 8N0 HW
> # cat /sys/bus/uart/devices/INTF001:00/modem_lines
> LE:RTS,CTS,

What if instead of exporting these to userspace we just set the defaults
based on the UartSerialBus() value? This way the user only needs to find the
right tty to pass to hciattach.

> kobject sysfs links:
> # ls -l /sys/bus/uart/devices
> INTF001:00 -> ../../../devices/platform/INTF000:00/INTF001:00
> # ls -l /sys/devices/platform/INTF000:00/INTF001:00
> subsystem -> ../../../../bus/uart
> host_node -> ../tty/ttyS0
> # ls -l /sys/devices/platform/INTF000:00/tty/ttyS0
> target_node -> ../../INTF001:00

And if we have enumerated the UART controller from ACPI (it is probably
attached to the platform bus) we can find the tty device it exports like:

/sys/bus/platform/devices/INTF000:00/tty/ttyS1/dev

Only thing that is missing then is the type of the connected device. That
can be extracted by following the firmware_node symlink:

# ls /sys/bus/platform/devices/INTF000\:00/firmware_node/
INTF001:00
...

# cat  /sys/bus/platform/devices/INTF000\:00/firmware_node/INTF001:00/hid
INTF001

This ACPI ID then can be used to find out what device it is.

Well, this is probably not the simplest solution :-) I'm just trying to
understand why we need to add a new bus type if only thing it adds is the
sysfs files.

> +/**
> + * uart_unregister_device - unregister a UART device
> + * @device: value returned from uart_register_device()
> + *
> + * Reverse effect of uart_register_device().
> + */
> +void uart_unregister_device(struct uart_device *udev)
> +{
> +	if (udev->adap)
> +		klist_del(&udev->klist_parent);
> +	sysfs_remove_link(&udev->dev.kobj, "host_node");
> +	if (udev->tty)
> +		sysfs_remove_link(&udev->tty->kobj, "target_node");
> +	device_unregister(&udev->dev);
> +}
> +EXPORT_SYMBOL_GPL(uart_unreigster_device);

Typo in the function name.

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [RFC PATCH 1/3] UART: Add UART subsystem as a bus.
  2012-12-04 18:54   ` Mika Westerberg
@ 2012-12-04 19:50     ` Alan Cox
  2012-12-05  6:20       ` Mika Westerberg
  2012-12-05  3:49     ` Zheng, Lv
  1 sibling, 1 reply; 71+ messages in thread
From: Alan Cox @ 2012-12-04 19:50 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: Lv Zheng, Len Brown, Rafael J Wysocki, Greg Kroah-Hartman,
	linux-acpi, linux-serial

> What if instead of exporting these to userspace we just set the
> defaults based on the UartSerialBus() value? This way the user only
> needs to find the right tty to pass to hciattach.

We should also make sure the types are generic because there are lots
of platforms that can use DT to describe devices usefully this way, and
there are types not in the ACPI spec (eg Loconet) that some apps could
do with finding.

> And if we have enumerated the UART controller from ACPI (it is
> probably attached to the platform bus) we can find the tty device it
> exports like:

The property should not be in any ACPI specific form or space - just
attach it directly to the tty from ACPI, DT, driver internal knowledge,
PCI id, whatever

Alan

^ permalink raw reply	[flat|nested] 71+ messages in thread

* RE: [RFC PATCH 1/3] UART: Add UART subsystem as a bus.
  2012-12-03 11:46   ` Alan Cox
@ 2012-12-05  3:37     ` Zheng, Lv
  0 siblings, 0 replies; 71+ messages in thread
From: Zheng, Lv @ 2012-12-05  3:37 UTC (permalink / raw)
  To: Alan Cox
  Cc: Brown, Len, Wysocki, Rafael J, Greg Kroah-Hartman, linux-acpi,
	linux-serial

OK, all uart_driver and serial_core.h stuff will be removed from the next revision.

Best regards/Lv Zheng

> -----Original Message-----
> From: linux-serial-owner@vger.kernel.org
> [mailto:linux-serial-owner@vger.kernel.org] On Behalf Of Alan Cox
> Sent: Monday, December 03, 2012 7:46 PM
> To: Zheng, Lv
> Cc: Brown, Len; Wysocki, Rafael J; Greg Kroah-Hartman;
> linux-acpi@vger.kernel.org; linux-serial@vger.kernel.org
> Subject: Re: [RFC PATCH 1/3] UART: Add UART subsystem as a bus.
> 
> > + * uart_tty_find - find the associated TTY device for the UART target
> > + *		   device
> > + * @drv: the low level UART driver
> > + * @dev: the parent physical device for the TTY devices
> > + * @line: the line number of the UART target device
> > + *
> 
> Our top level abstraction is struct tty and struct tty_port. There is nothing
> requiring a serial tty is using the uart helper layer, nor should there be, so your
> code needs to support devices not using the uart layer. Otherwise it looks quite
> reasonable.
> 
> our 3.7 tty layer has sysfs nodes on the tty which may also help
> 
> Alan
> --
> To unsubscribe from this list: send the line "unsubscribe linux-serial" in the body
> of a message to majordomo@vger.kernel.org More majordomo info at
> http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 71+ messages in thread

* RE: [RFC PATCH 1/3] UART: Add UART subsystem as a bus.
  2012-12-04 18:54   ` Mika Westerberg
  2012-12-04 19:50     ` Alan Cox
@ 2012-12-05  3:49     ` Zheng, Lv
  1 sibling, 0 replies; 71+ messages in thread
From: Zheng, Lv @ 2012-12-05  3:49 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: Brown, Len, Wysocki, Rafael J, Greg Kroah-Hartman, Alan Cox,
	linux-acpi, linux-serial

> On Mon, Dec 03, 2012 at 11:39:59AM +0800, Lv Zheng wrote:
> > kobject attribute files:
> > # cat /sys/bus/uart/devices/INTF001:00/modalias
> > uart:INTF001:00
> > # cat /sys/bus/uart/devices/INTF001:00/tty_dev
> > ttyS0
> > # cat /sys/bus/uart/devices/INTF001:00/tty_attrs
> > 115200 8N0 HW
> > # cat /sys/bus/uart/devices/INTF001:00/modem_lines
> > LE:RTS,CTS,
> 
> What if instead of exporting these to userspace we just set the defaults based
> on the UartSerialBus() value? This way the user only needs to find the right tty
> to pass to hciattach.
> 
> > kobject sysfs links:
> > # ls -l /sys/bus/uart/devices
> > INTF001:00 -> ../../../devices/platform/INTF000:00/INTF001:00
> > # ls -l /sys/devices/platform/INTF000:00/INTF001:00
> > subsystem -> ../../../../bus/uart
> > host_node -> ../tty/ttyS0
> > # ls -l /sys/devices/platform/INTF000:00/tty/ttyS0
> > target_node -> ../../INTF001:00
> 
> And if we have enumerated the UART controller from ACPI (it is probably
> attached to the platform bus) we can find the tty device it exports like:
> 
> /sys/bus/platform/devices/INTF000:00/tty/ttyS1/dev
> 
> Only thing that is missing then is the type of the connected device. That can be
> extracted by following the firmware_node symlink:
> 
> # ls /sys/bus/platform/devices/INTF000\:00/firmware_node/
> INTF001:00
> ...
> 
> # cat
> /sys/bus/platform/devices/INTF000\:00/firmware_node/INTF001:00/hid
> INTF001
> 
> This ACPI ID then can be used to find out what device it is.
> 
> Well, this is probably not the simplest solution :-) I'm just trying to understand
> why we need to add a new bus type if only thing it adds is the sysfs files.

In our team, we've been discussing this solution like your suggestion for some times.
ACPI UART enumeration can be done in this way without kernel modifications.

IMO:
1. tty_device is current used as "host" side device, you can find .pm support in most of the serial drivers.
  They are always the codes turning off/on the clock of the host controller.
  If we want to support PM for a target device, there is no such node in the kernel device hierarchy.
2. firmware_node is based on the "likely deprecated" acpi_device solution, I need to find another one. Maybe this is still a shadow device.
  Like dmi-id device, a shadow device exporting attributes and generating uevents for the user space drivers may be helpful.
  It would be better if the PM support of the target devices can benefit from the existence of the shadow uart target devices.

> > +/**
> > + * uart_unregister_device - unregister a UART device
> > + * @device: value returned from uart_register_device()
> > + *
> > + * Reverse effect of uart_register_device().
> > + */
> > +void uart_unregister_device(struct uart_device *udev) {
> > +	if (udev->adap)
> > +		klist_del(&udev->klist_parent);
> > +	sysfs_remove_link(&udev->dev.kobj, "host_node");
> > +	if (udev->tty)
> > +		sysfs_remove_link(&udev->tty->kobj, "target_node");
> > +	device_unregister(&udev->dev);
> > +}
> > +EXPORT_SYMBOL_GPL(uart_unreigster_device);
> 
> Typo in the function name.

Fixed...

Thanks and best regards/Lv Zheng

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH v2 0/4] ACPI/UART: Add ACPI 5.0 enueration support for UART.
  2012-12-03  3:39 [RFC PATCH 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
                   ` (2 preceding siblings ...)
  2012-12-03  3:40 ` [RFC PATCH 3/3] UART: Add dummy devices to test the enumeration Lv Zheng
@ 2012-12-05  3:51 ` Lv Zheng
  2012-12-05  3:51   ` [PATCH v2 1/4] UART: Add UART subsystem as a bus Lv Zheng
                     ` (3 more replies)
  2012-12-06  9:21 ` [RFC PATCH v3 0/4] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
                   ` (6 subsequent siblings)
  10 siblings, 4 replies; 71+ messages in thread
From: Lv Zheng @ 2012-12-05  3:51 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox,
	Mika Westerberg
  Cc: linux-acpi, linux-serial, Lv Zheng

ACPI 5.0 specification introduces enumeration support for SPB buses. This
patch set adds the UART serial bus enumeration support to Linux using such
mechanism.

NOTE: The [PATCH 4/4] is only for the demonstration purpose and should not
      be merged into any of the published Linux source tree.

Lv Zheng (4):
  UART: Add UART subsystem as a bus.
  ACPI / UART: Add ACPI enumeration support for UART bus.
  UART / 8250: Add declearation of serial8250 driver.
  UART: Add dummy devices to test the enumeration.

 drivers/acpi/Kconfig                 |    7 +
 drivers/acpi/Makefile                |    1 +
 drivers/acpi/acpi_uart.c             |  262 +++++++++++++++++++++
 drivers/acpi/scan.c                  |    1 +
 drivers/tty/serial/8250/8250.c       |   17 +-
 drivers/tty/serial/8250/8250_dummy.c |  129 ++++++++++
 drivers/tty/serial/8250/Kconfig      |   10 +
 drivers/tty/serial/8250/Makefile     |    1 +
 drivers/tty/serial/Makefile          |    2 +-
 drivers/tty/serial/serial_bus.c      |  429 ++++++++++++++++++++++++++++++++++
 include/linux/acpi_uart.h            |   40 ++++
 include/linux/mod_devicetable.h      |    5 +
 include/linux/serial_8250.h          |    2 +
 include/linux/serial_bus.h           |   80 +++++++
 14 files changed, 982 insertions(+), 4 deletions(-)
 create mode 100644 drivers/acpi/acpi_uart.c
 create mode 100644 drivers/tty/serial/8250/8250_dummy.c
 create mode 100644 drivers/tty/serial/serial_bus.c
 create mode 100644 include/linux/acpi_uart.h
 create mode 100644 include/linux/serial_bus.h

-- 
1.7.10


^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH v2 1/4] UART: Add UART subsystem as a bus.
  2012-12-05  3:51 ` [PATCH v2 0/4] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
@ 2012-12-05  3:51   ` Lv Zheng
  2012-12-05  3:51   ` [PATCH v2 2/4] ACPI / UART: Add ACPI enumeration support for UART bus Lv Zheng
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 71+ messages in thread
From: Lv Zheng @ 2012-12-05  3:51 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox,
	Mika Westerberg
  Cc: linux-acpi, linux-serial, Lv Zheng

Tranditional UARTs are used as communication pipes between the hosts
while the modern computing systems equipped with HSU (High Speed UARTs)
would connect on-board target devices using the UART ports. The role of
the UART controllers is changed from the communication facility to the
platform bus.

In the recent ACPI 5.0 specification updates, firmwares are provided the
possibilities to enumerate the UART target devices known to the platform
vendors.
Thus there are the needs for enumerating the UART target devices:
1. hotplug uevent
2. serial configuration
Currently, only serial cards on the specific bus (ex. PCMCIA) can be
enumerated and userspace can obtain the hotplug event of the UART target
devices. Linux kernel is lack of an overall enumeration mechanism for
UART target devices.
In order to send uevent, a device need to be a class device or a bus
device. This patch introduces a bus_type subsystem to manage the new
UART target device type for the purpose of being ready for the possible
future extensions.
When the UART target devices are created, userspace uevent rules can
pass the creation details to the userspace driver managers
(ex. hciattach). Here is an example of the uevents and exported
attributes of these new UART target devices:

Test DSDT (dummy UART host adapter INTF000 and target device INTF001):
  Device (UA00)
  {
      Name (_HID, "INTF000")  // _HID: Hardware ID
      Name (RBUF, ResourceTemplate ()
      {
          Memory32Fixed (ReadWrite,
              0x00000000,         // Address Base
              0x00001000)         // Address Length
      })
      Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
      {
          Return (RBUF)
      }
      Method (_STA, 0, NotSerialized)  // _STA: Status
      {
          Return (0x0F)
      }
      Device (BTH0)
      {
          Name (_HID, "INTF001")  // _HID: Hardware ID
          Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
          {
              Name (UBUF, ResourceTemplate ()
              {
                  UartSerialBus (0x0001C200, DataBitsEight, StopBitsOne,
                      0xC0, LittleEndian, ParityTypeNone, FlowControlHardware,
                      0x0020, 0x0020, "\\_SB.PCI0.UA00",
                      0x00, ResourceConsumer, ,
                      )
              })
              Return (UBUF)
          }
          Method (_STA, 0, NotSerialized)  // _STA: Status
          {
              Return (0x0F)
          }
      }
  }

uevent and environments:
KERNEL[252.443458] add      /bus/uart (bus)
ACTION=add
DEVPATH=/bus/uart
SEQNUM=1142
SUBSYSTEM=bus

KERNEL[268.491709] add      /devices/platform/INTF000:00/INTF001:00 (uart)
ACTION=add
DEVPATH=/devices/platform/INTF000:00/INTF001:00
DEVTYPE=uart_device
MODALIAS=uart:INTF001:00
SEQNUM=1144
SUBSYSTEM=uart

kobject attribute files:
# cat /sys/bus/uart/devices/INTF001:00/modalias
uart:INTF001:00
# cat /sys/bus/uart/devices/INTF001:00/tty_dev
ttyS0
# cat /sys/bus/uart/devices/INTF001:00/tty_attrs
115200 8N0 HW
# cat /sys/bus/uart/devices/INTF001:00/modem_lines
LE:RTS,CTS,

kobject sysfs links:
# ls -l /sys/bus/uart/devices
INTF001:00 -> ../../../devices/platform/INTF000:00/INTF001:00
# ls -l /sys/devices/platform/INTF000:00/INTF001:00
subsystem -> ../../../../bus/uart
host_node -> ../tty/ttyS0
# ls -l /sys/devices/platform/INTF000:00/tty/ttyS0
target_node -> ../../INTF001:00

History:
v1:
  1. Add uart bus type subsystem.
  2. Add physical uart target device (uart_device).
  3. Add logical uart host adapter (klist).
  4. Add uart target device attributes.
  5. Create tty_device<->uart_device links.
v2.
  1. Change uart driver related stuff to tty driver.

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/tty/serial/Makefile     |    2 +-
 drivers/tty/serial/serial_bus.c |  412 +++++++++++++++++++++++++++++++++++++++
 include/linux/mod_devicetable.h |    5 +
 include/linux/serial_bus.h      |   75 +++++++
 4 files changed, 493 insertions(+), 1 deletion(-)
 create mode 100644 drivers/tty/serial/serial_bus.c
 create mode 100644 include/linux/serial_bus.h

diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 4f694da..4400521 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -2,7 +2,7 @@
 # Makefile for the kernel serial device drivers.
 #
 
-obj-$(CONFIG_SERIAL_CORE) += serial_core.o
+obj-$(CONFIG_SERIAL_CORE) += serial_core.o serial_bus.o
 obj-$(CONFIG_SERIAL_21285) += 21285.o
 
 # These Sparc drivers have to appear before others such as 8250
diff --git a/drivers/tty/serial/serial_bus.c b/drivers/tty/serial/serial_bus.c
new file mode 100644
index 0000000..afbb885
--- /dev/null
+++ b/drivers/tty/serial/serial_bus.c
@@ -0,0 +1,412 @@
+/*
+ * serial_bus.c - UART bus implementation
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+/*
+ * Tranditional UARTs are used as communication pipes between the hosts
+ * while the modern computing systems equipped with HSU (High Speed UARTs)
+ * would connect on-board target devices using the UART ports. The role of
+ * the UART controllers are changed from the communication facility to the
+ * platform bus.
+ *
+ * UART target devices are created in the kernel as struct uart_device.
+ * It is defined for the following purposes:
+ * 1. Sending hotplug notifications to the userspace
+ * 2. Exporting serial configuration parameters to the userspace
+ * 3. Allowing target device based PM to be added easily
+ *
+ */
+
+#include <linux/serial.h>
+#include <linux/serial_bus.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+
+struct uart_match {
+	dev_t devt;
+};
+
+static int do_uart_tty_find(struct device *dev, void *data)
+{
+	struct uart_match *match = data;
+	return dev->devt == match->devt;
+}
+
+/**
+ * uart_tty_find - find the tty device using the line number
+ * @drv: the tty host driver
+ * @line: the line number of the tty device
+ * @dev: the physical uart host adapter
+ *
+ * Return a matching tty device on the host adapter.
+ */
+struct device *uart_tty_find(struct tty_driver *drv, unsigned int line,
+			     struct device *dev)
+{
+	struct uart_match match;
+
+	match.devt = MKDEV(drv->major, drv->minor_start + line);
+
+	return device_find_child(dev, &match, do_uart_tty_find);
+}
+EXPORT_SYMBOL_GPL(uart_tty_find);
+
+/**
+ * uart_tty_name - get the name of the tty device according to the line
+ *                 number
+ * @drv: the tty host driver
+ * @line: the line number of the tty device
+ * @p: pointer to the buffer containing the returned name
+ *
+ * Return the name of the tty device.
+ */
+void uart_tty_name(struct tty_driver *drv, unsigned int line, char *p)
+{
+	BUG_ON(!drv || !p);
+
+	if (drv->flags & TTY_DRIVER_UNNUMBERED_NODE)
+		strcpy(p, drv->name);
+	else
+		sprintf(p, "%s%d", drv->name, line + drv->name_base);
+}
+EXPORT_SYMBOL_GPL(uart_tty_name);
+
+static ssize_t uart_dev_show_name(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct uart_device *udev = to_uart_device(dev);
+	return sprintf(buf, "%s\n", udev->name);
+}
+
+static ssize_t uart_dev_show_modalias(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct uart_device *udev = to_uart_device(dev);
+	return sprintf(buf, "%s%s\n", UART_MODULE_PREFIX, udev->name);
+}
+
+static ssize_t uart_dev_show_tty_dev(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct uart_device *udev = to_uart_device(dev);
+	return sprintf(buf, "%s\n", udev->tty_name);
+}
+
+static ssize_t uart_dev_show_tty_attrs(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct uart_device *udev = to_uart_device(dev);
+	int len = 0;
+
+	/* baud rate */
+	len += sprintf(buf+len, "%d ", udev->baud);
+
+	/* data bits */
+	switch (udev->cflag & CSIZE) {
+	case CS5:
+		len += sprintf(buf+len, "5");
+		break;
+	case CS6:
+		len += sprintf(buf+len, "6");
+		break;
+	case CS7:
+		len += sprintf(buf+len, "7");
+		break;
+	case CS8:
+	default:
+		len += sprintf(buf+len, "8");
+		break;
+	}
+
+	/* parity */
+	if (udev->cflag & PARODD)
+		len += sprintf(buf+len, "O");
+	else if (udev->cflag & PARENB)
+		len += sprintf(buf+len, "E");
+	else
+		len += sprintf(buf+len, "N");
+
+	/* stop bits */
+	len += sprintf(buf+len, "%d", udev->cflag & CSTOPB ? 1 : 0);
+
+	/* HW/SW control */
+	if (udev->cflag & CRTSCTS)
+		len += sprintf(buf+len, " HW");
+	if ((udev->iflag & (IXON|IXOFF|IXANY)) != 0)
+		len += sprintf(buf+len, " SW");
+
+	len += sprintf(buf+len, "\n");
+	return len;
+}
+
+static ssize_t uart_dev_show_modem_lines(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct uart_device *udev = to_uart_device(dev);
+	int len = 0;
+
+	/* endian */
+	if (udev->mctrl & TIOCM_LE)
+		len += sprintf(buf+len, "LE:");
+	else
+		len += sprintf(buf+len, "BE:");
+
+	/* terminal lines */
+	if (udev->mctrl & TIOCM_DTR)
+		len += sprintf(buf+len, "DTR,");
+	if (udev->mctrl & TIOCM_RTS)
+		len += sprintf(buf+len, "RTS,");
+
+	/* modem lines */
+	if (udev->mctrl & TIOCM_CTS)
+		len += sprintf(buf+len, "CTS,");
+	if (udev->mctrl & TIOCM_CAR)
+		len += sprintf(buf+len, "CAR,");
+	if (udev->mctrl & TIOCM_RNG)
+		len += sprintf(buf+len, "RNG,");
+	if (udev->mctrl & TIOCM_DSR)
+		len += sprintf(buf+len, "DSR,");
+
+	len += sprintf(buf+len, "\n");
+	return len;
+}
+
+static DEVICE_ATTR(name, S_IRUGO, uart_dev_show_name, NULL);
+static DEVICE_ATTR(modalias, S_IRUGO, uart_dev_show_modalias, NULL);
+static DEVICE_ATTR(tty_dev, S_IRUGO, uart_dev_show_tty_dev, NULL);
+static DEVICE_ATTR(tty_attrs, S_IRUGO, uart_dev_show_tty_attrs, NULL);
+static DEVICE_ATTR(modem_lines, S_IRUGO, uart_dev_show_modem_lines, NULL);
+
+static struct attribute *uart_dev_attrs[] = {
+	&dev_attr_name.attr,
+	/* coldplug: modprobe $(cat .../modalias) */
+	&dev_attr_modalias.attr,
+	&dev_attr_tty_dev.attr,
+	&dev_attr_tty_attrs.attr,
+	&dev_attr_modem_lines.attr,
+	NULL,
+};
+
+static struct attribute_group uart_dev_attr_group = {
+	.attrs	= uart_dev_attrs,
+};
+
+static const struct attribute_group *uart_dev_attr_groups[] = {
+	&uart_dev_attr_group,
+	NULL,
+};
+
+#ifdef CONFIG_HOTPLUG
+static int uart_device_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct uart_device *udev = to_uart_device(dev);
+
+	if (add_uevent_var(env, "MODALIAS=%s%s",
+			   UART_MODULE_PREFIX, udev->name))
+		return -ENOMEM;
+
+	dev_dbg(dev, "uevent\n");
+	return 0;
+}
+#else
+#define uart_device_uevent	NULL
+#endif
+
+static void uart_device_release(struct device *dev)
+{
+	struct uart_device *udev = to_uart_device(dev);
+
+	put_device(udev->tty);
+	kfree(udev);
+}
+
+struct device_type uart_device_type = {
+	.name		= "uart_device",
+	.groups		= uart_dev_attr_groups,
+	.uevent		= uart_device_uevent,
+	.release	= uart_device_release,
+};
+EXPORT_SYMBOL_GPL(uart_device_type);
+
+/**
+ * uart_register_device - register a physical uart target device
+ * @adap: the logical uart host adapter
+ * @dev: the physical uart host adapter
+ * @drv: the tty host driver
+ * @info: the uart target device description
+ *
+ * Initialize and add a physical uart target device.
+ *
+ * This returns the new uart target device, which may be saved for later use
+ * with uart_unregister_device; or NULL to indicate an error.
+ */
+struct uart_device *uart_register_device(struct klist *adap,
+					 struct device *dev,
+					 struct tty_driver *drv,
+					 struct uart_board_info const *info)
+{
+	struct uart_device *udev;
+	struct device *tty;
+	int status;
+
+	BUG_ON((!adap && !dev) || !drv);
+
+	udev = kzalloc(sizeof(struct uart_device), GFP_KERNEL);
+	if (!udev)
+		return NULL;
+
+	strlcpy(udev->name, info->type, sizeof(udev->name));
+
+	udev->baud = info->baud;
+	udev->cflag = info->cflag;
+	udev->iflag = info->iflag;
+	udev->mctrl = info->mctrl;
+
+	udev->dev.parent = dev;
+	udev->dev.bus = &uart_bus_type;
+	udev->dev.type = &uart_device_type;
+
+	tty = uart_tty_find(drv, info->line, dev);
+	if (!tty) {
+		dev_err(dev, "Cannot find associated tty device at line %d.\n",
+			info->line);
+		goto fail;
+	}
+	udev->tty = get_device(tty);
+	uart_tty_name(drv, info->line, udev->tty_name);
+
+	dev_set_name(&udev->dev, "%s", udev->name);
+	status = device_register(&udev->dev);
+	if (status) {
+		dev_err(dev,
+			"Failed to register uart target device at line %d.\n",
+			info->line);
+		goto fail2;
+	}
+	dev_info(&udev->dev, "Registered at line %d.\n", info->line);
+
+	status = sysfs_create_link(&tty->kobj, &udev->dev.kobj, "target_node");
+	status = sysfs_create_link(&udev->dev.kobj, &tty->kobj, "host_node");
+
+	if (adap) {
+		udev->adap = adap;
+		klist_add_tail(&udev->klist_parent, adap);
+	}
+
+	return udev;
+
+fail2:
+	put_device(tty);
+fail:
+	kfree(udev);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(uart_register_device);
+
+/**
+ * uart_unregister_device - unregister a physical uart target device
+ * @udev: the physical uart target device
+ *
+ * Reverse effect of uart_register_device().
+ */
+void uart_unregister_device(struct uart_device *udev)
+{
+	if (!udev)
+		return;
+	dev_info(&udev->dev, "Unregistering...\n");
+
+	if (udev->adap)
+		klist_del(&udev->klist_parent);
+	sysfs_remove_link(&udev->dev.kobj, "host_node");
+	if (udev->tty)
+		sysfs_remove_link(&udev->tty->kobj, "target_node");
+	device_unregister(&udev->dev);
+}
+EXPORT_SYMBOL_GPL(uart_unregister_device);
+
+static void klist_uart_get(struct klist_node *n)
+{
+	struct uart_device *udev = uart_device_from_parent(n);
+	get_device(&udev->dev);
+}
+
+static void klist_uart_put(struct klist_node *n)
+{
+	struct uart_device *udev = uart_device_from_parent(n);
+	put_device(&udev->dev);
+}
+
+static struct uart_device *uart_next_device(struct klist_iter *i)
+{
+	struct klist_node *n = klist_next(i);
+	return n ? uart_device_from_parent(n) : NULL;
+}
+
+/**
+ * uart_add_adapter - register a logical uart host adapter
+ * @adap: the logical uart host adapter
+ *
+ * Initialize a logical uart host adapter.
+ */
+int uart_add_adapter(struct klist *adap)
+{
+	klist_init(adap, klist_uart_get, klist_uart_put);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(uart_add_adapter);
+
+/**
+ * uart_del_adapter - unregister a logical uart host adapter
+ * @adap: the logical uart host adapter
+ *
+ * Reverse effect of uart_add_adapter().
+ */
+void uart_del_adapter(struct klist *adap)
+{
+	struct klist_iter i;
+	struct uart_device *udev;
+
+	if (!adap)
+		return;
+
+	klist_iter_init(adap, &i);
+	while ((udev = uart_next_device(&i)))
+		uart_unregister_device(udev);
+	klist_iter_exit(&i);
+}
+EXPORT_SYMBOL_GPL(uart_del_adapter);
+
+struct bus_type uart_bus_type = {
+	.name		= "uart",
+};
+EXPORT_SYMBOL_GPL(uart_bus_type);
+
+static int __init uart_bus_init(void)
+{
+	int retval;
+
+	retval = bus_register(&uart_bus_type);
+	if (retval)
+		return retval;
+
+	return 0;
+}
+
+static void __exit uart_bus_exit(void)
+{
+	bus_unregister(&uart_bus_type);
+}
+
+subsys_initcall(uart_bus_init);
+module_exit(uart_bus_exit);
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index fed3def..28df140 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -455,6 +455,11 @@ struct spi_device_id {
 			__attribute__((aligned(sizeof(kernel_ulong_t))));
 };
 
+/* uart */
+
+#define UART_NAME_SIZE	32
+#define UART_MODULE_PREFIX "uart:"
+
 /* dmi */
 enum dmi_field {
 	DMI_NONE,
diff --git a/include/linux/serial_bus.h b/include/linux/serial_bus.h
new file mode 100644
index 0000000..6d66fe2
--- /dev/null
+++ b/include/linux/serial_bus.h
@@ -0,0 +1,75 @@
+/*
+ * serial_bus.h - UART bus implementation
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+#ifndef LINUX_SERIAL_BUS_H
+#define LINUX_SERIAL_BUS_H
+
+#include <linux/compiler.h>
+#include <linux/tty.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+
+struct uart_device;
+
+/*
+ * UART Bus
+ */
+struct uart_board_info {
+	char type[UART_NAME_SIZE];
+	unsigned int line;	/* port index */
+	unsigned int cflag;	/* termio cflag */
+	unsigned int iflag;	/* termio iflag */
+	unsigned int mctrl;	/* modem ctrl settings */
+	unsigned int baud;
+	int irq;
+};
+
+extern struct bus_type uart_bus_type;
+
+int uart_add_adapter(struct klist *adap);
+void uart_del_adapter(struct klist *adap);
+
+struct uart_device {
+	char			name[UART_NAME_SIZE];
+	char			tty_name[64];
+	unsigned int		cflag;	/* termio cflag */
+	unsigned int		iflag;	/* termio iflag */
+	unsigned int		mctrl;	/* modem ctrl settings */
+	unsigned int		baud;
+	struct device		dev;
+	struct device		*tty;
+	struct klist_node	klist_parent;
+	struct klist		*adap;	/* set for multi-port adapter */
+};
+
+extern struct device_type uart_device_type;
+
+#define is_uart_device(d) ((d) && (d)->type == &uart_device_type)
+#define to_uart_device(d) container_of(d, struct uart_device, dev)
+#define uart_device_from_parent(n)	\
+	container_of(n, struct uart_device, klist_parent)
+
+static inline struct uart_device *uart_verify_device(struct device *dev)
+{
+	return is_uart_device(dev) ? to_uart_device(dev) : NULL;
+}
+
+struct uart_device *uart_register_device(struct klist *adap,
+					 struct device *dev,
+					 struct tty_driver *drv,
+					 struct uart_board_info const *info);
+void uart_unregister_device(struct uart_device *udev);
+
+struct device *uart_tty_find(struct tty_driver *drv, unsigned int line,
+			     struct device *dev);
+void uart_tty_name(struct tty_driver *driver, unsigned int line, char *p);
+
+#endif /* LINUX_SERIAL_BUS_H */
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v2 2/4] ACPI / UART: Add ACPI enumeration support for UART bus.
  2012-12-05  3:51 ` [PATCH v2 0/4] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
  2012-12-05  3:51   ` [PATCH v2 1/4] UART: Add UART subsystem as a bus Lv Zheng
@ 2012-12-05  3:51   ` Lv Zheng
  2012-12-05  3:51   ` [PATCH v2 3/4] UART / 8250: Add declearation of serial8250 driver Lv Zheng
  2012-12-05  3:52   ` [PATCH v2 4/4] UART: Add dummy devices to test the enumeration Lv Zheng
  3 siblings, 0 replies; 71+ messages in thread
From: Lv Zheng @ 2012-12-05  3:51 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox,
	Mika Westerberg
  Cc: linux-acpi, linux-serial, Lv Zheng

ACPI 5.0 specification introduces the methods of enumerating the target
devices connected on the serial buses.
This patch follows the specification, implementing such UART enumeration
machanism for the Linux.

In order to use this UART device enumeration mechanism, driver writers
are required to call the following APIs:
1. APIs called _after_ the creation of the uart ports:
   adap = acpi_uart_register_devices(driver, adap, parent, line);
   Where:
    driver: the low level UART driver
    parent: the physical device of the UART ports
    adap: the management list of the target devices, can be set as NULL
    line: the line number of the target device
2. APIs called _before_ the deletion of the uart ports:
   acpi_uart_unregister_devices(adap);
   Where:
    adap: the management list of the UART target devices

NOTE: If the driver writer has already created the management list for
      the UART target devices, the adap parameter can be set to the
      already created non-NULL value.
NOTE: The ACPI 5.0 specification assumes one physical device per-port.
      In this situation, the line parameter might be set to 0 when the
      acpi_uart_register_devices() is called.
      This patch set can also support the multi-port UART adapters,
      where the line should be set as ACPI_UART_LINE_UNKNOWN.

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/acpi/Kconfig      |    7 ++
 drivers/acpi/Makefile     |    1 +
 drivers/acpi/acpi_uart.c  |  262 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/acpi_uart.h |   40 +++++++
 4 files changed, 310 insertions(+)
 create mode 100644 drivers/acpi/acpi_uart.c
 create mode 100644 include/linux/acpi_uart.h

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 0300bf6..d40203f 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -187,6 +187,12 @@ config ACPI_I2C
 	help
 	  ACPI I2C enumeration support.
 
+config ACPI_UART
+	def_tristate SERIAL_CORE
+	depends on SERIAL_CORE
+	help
+	  ACPI UART enumeration support.
+
 config ACPI_PROCESSOR
 	tristate "Processor"
 	select THERMAL
@@ -200,6 +206,7 @@ config ACPI_PROCESSOR
 
 	  To compile this driver as a module, choose M here:
 	  the module will be called processor.
+
 config ACPI_IPMI
 	tristate "IPMI"
 	depends on EXPERIMENTAL && IPMI_SI && IPMI_HANDLER
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 2a4502b..784f332 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -71,6 +71,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS)	+= ec_sys.o
 obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
 obj-$(CONFIG_ACPI_BGRT)		+= bgrt.o
 obj-$(CONFIG_ACPI_I2C)		+= acpi_i2c.o
+obj-$(CONFIG_ACPI_UART)		+= acpi_uart.o
 
 # processor has its own "processor." module_param namespace
 processor-y			:= processor_driver.o processor_throttling.o
diff --git a/drivers/acpi/acpi_uart.c b/drivers/acpi/acpi_uart.c
new file mode 100644
index 0000000..74caf9c
--- /dev/null
+++ b/drivers/acpi/acpi_uart.c
@@ -0,0 +1,262 @@
+/*
+ * acpi_uart.c - ACPI UART enumeration support
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+#include <linux/init.h>
+#include <linux/serial_bus.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/acpi_uart.h>
+#include <acpi/acpi.h>
+#include <acpi/acpi_bus.h>
+
+
+static int
+acpi_uart_add_resources(struct acpi_resource *ares, void *context)
+{
+	struct uart_board_info *info = context;
+
+	if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
+		struct acpi_resource_uart_serialbus *sb;
+
+		sb = &ares->data.uart_serial_bus;
+		if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_UART)
+			return 1;
+
+		/* baud rate */
+		info->baud = sb->default_baud_rate;
+
+		/* data bits */
+		info->cflag &= ~CSIZE;
+		switch (sb->data_bits) {
+		case ACPI_UART_5_DATA_BITS:
+			info->cflag |= CS5;
+			break;
+		case ACPI_UART_6_DATA_BITS:
+			info->cflag |= CS6;
+			break;
+		case ACPI_UART_7_DATA_BITS:
+			info->cflag |= CS7;
+			break;
+		case ACPI_UART_8_DATA_BITS:
+		default:
+			info->cflag |= CS8;
+			break;
+		}
+
+		/* parity */
+		info->cflag &= ~(PARENB | PARODD);
+		if (sb->parity == ACPI_UART_PARITY_EVEN)
+			info->cflag |= PARENB;
+		else if (sb->parity == ACPI_UART_PARITY_ODD)
+			info->cflag |= (PARENB | PARODD);
+
+		/* stop bits */
+		if (sb->stop_bits == ACPI_UART_2_STOP_BITS)
+			info->cflag |= CSTOPB;
+		else
+			info->cflag &= ~CSTOPB;
+
+		/* HW control */
+		if (sb->flow_control & ACPI_UART_FLOW_CONTROL_HW)
+			info->cflag |= CRTSCTS;
+		else
+			info->cflag &= ~CRTSCTS;
+
+		/* SW control */
+		if (sb->flow_control & ACPI_UART_FLOW_CONTROL_XON_XOFF)
+			info->iflag |= (IXON | IXOFF);
+		else
+			info->iflag &= ~(IXON|IXOFF|IXANY);
+
+		/* endianess */
+		if (sb->endian == ACPI_UART_LITTLE_ENDIAN)
+			info->mctrl |= TIOCM_LE;
+		else
+			info->mctrl &= ~TIOCM_LE;
+
+		/* terminal lines */
+		if (sb->lines_enabled & ACPI_UART_DATA_TERMINAL_READY)
+			info->mctrl |= TIOCM_DTR;
+		else
+			info->mctrl &= ~TIOCM_DTR;
+		if (sb->lines_enabled & ACPI_UART_REQUEST_TO_SEND)
+			info->mctrl |= TIOCM_RTS;
+		else
+			info->mctrl &= ~TIOCM_RTS;
+
+		/* modem lines */
+		if (sb->lines_enabled & ACPI_UART_CLEAR_TO_SEND)
+			info->mctrl |= TIOCM_CTS;
+		else
+			info->mctrl &= ~TIOCM_CTS;
+		if (sb->lines_enabled & ACPI_UART_CARRIER_DETECT)
+			info->mctrl |= TIOCM_CAR;
+		else
+			info->mctrl &= ~TIOCM_CAR;
+		if (sb->lines_enabled & ACPI_UART_RING_INDICATOR)
+			info->mctrl |= TIOCM_RNG;
+		else
+			info->mctrl &= ~TIOCM_RNG;
+		if (sb->lines_enabled & ACPI_UART_DATA_SET_READY)
+			info->mctrl |= TIOCM_DSR;
+		else
+			info->mctrl &= ~TIOCM_DSR;
+	} else if (info->irq < 0) {
+		struct resource r;
+
+		if (acpi_dev_resource_interrupt(ares, 0, &r))
+			info->irq = r.start;
+	}
+
+	return 1;
+}
+
+struct acpi_uart_walk {
+	struct tty_driver *drv;
+	unsigned int line;
+	struct device *parent;
+};
+
+static acpi_status acpi_uart_add_device(acpi_handle handle, u32 level,
+					void *context, void **return_value)
+{
+	struct acpi_uart_walk *walk = context;
+	struct tty_driver *drv = walk->drv;
+	unsigned int line = walk->line;
+	struct device *parent = walk->parent;
+	struct acpi_device_info *info;
+	struct uart_board_info board_info;
+	struct acpi_device *adev;
+	struct list_head resource_list;
+	int ret;
+	acpi_status status;
+	struct klist *adap = *return_value;
+	struct uart_device *udev;
+
+	BUG_ON(!parent || !adap || !drv);
+
+	/*
+	 * Current BIOS will create physical adapter DEVICE per port.
+	 * This implementation assumes multi-port DEVICE will use _ADR
+	 * as serial port line number though there is no such explicit
+	 * proof for this assumption in the ACPI specification.
+	 *
+	 * NOTE: There is no real BIOS implementation using _ADR as line
+	 *       number.
+	 */
+	if (line == ACPI_UART_LINE_UNKNOWN) {
+		status = acpi_get_object_info(handle, &info);
+		if (ACPI_FAILURE(status))
+			return AE_OK;
+		if (info->valid & ACPI_VALID_ADR)
+			line = info->address;
+	}
+
+	if (line == ACPI_UART_LINE_UNKNOWN)
+		return AE_OK;
+	if (acpi_bus_get_device(handle, &adev))
+		return AE_OK;
+	if (acpi_bus_get_status(adev) || !adev->status.present)
+		return AE_OK;
+
+	memset(&board_info, 0, sizeof(board_info));
+	board_info.irq = -1;
+	board_info.line = line;
+
+	INIT_LIST_HEAD(&resource_list);
+	ret = acpi_dev_get_resources(adev, &resource_list,
+				     acpi_uart_add_resources, &board_info);
+	acpi_dev_free_resource_list(&resource_list);
+
+	if (ret < 0 || !board_info.baud)
+		return AE_OK;
+
+	strlcpy(board_info.type, dev_name(&adev->dev), sizeof(board_info.type));
+
+	udev = uart_register_device(adap, parent, drv, &board_info);
+	if (!udev) {
+		dev_err(&adev->dev, "failed to add #%d uart device from ACPI\n",
+			line);
+		return AE_OK;
+	}
+
+	return AE_OK;
+}
+
+/**
+ * acpi_uart_register_devices - register the uart slave devices behind the
+ *                              uart host adapter
+ * @adap: the logical uart host adapter
+ * @dev: the physical uart host adapter
+ * @drv: the tty host driver
+ * @line: the serial line number
+ *
+ * Enumerate all uart slave devices behind the adapter by walking the ACPI
+ * namespace. When a device is found, it will be added to the Linux device
+ * model and bound to the corresponding ACPI handle.
+ * If the physical device is a multiple port device, line should be -1.
+ * If the physical device is a single port device, line should be the line
+ * number.
+ */
+struct klist *acpi_uart_register_devices(struct klist *adap,
+					 struct device *parent,
+					 struct tty_driver *drv,
+					 unsigned int line)
+{
+	acpi_handle handle;
+	acpi_status status;
+	struct klist *klist = NULL;
+	struct acpi_uart_walk walk = {drv, line, parent};
+
+	BUG_ON(!drv || !parent);
+
+	handle = ACPI_HANDLE(parent);
+	if (!handle)
+		return NULL;
+
+	if (!adap) {
+		klist = kzalloc(sizeof(struct klist), GFP_KERNEL);
+		if (!klist)
+			return NULL;
+		(void)uart_add_adapter(klist);
+		adap = klist;
+	}
+
+	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
+				     acpi_uart_add_device, NULL, &walk,
+				     (void **)&adap);
+	if (ACPI_FAILURE(status)) {
+		dev_warn(parent, "failed to enumerate UART slaves\n");
+		goto fail;
+	}
+
+	return adap;
+
+fail:
+	uart_del_adapter(klist);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(acpi_uart_register_devices);
+
+/**
+ * acpi_uart_register_devices - unregister the uart slave devices behind the
+ *                              uart host adapter
+ * @adap: the logical uart host adapter
+ *
+ * Reverse effect of acpi_uart_register_devices().
+ */
+void acpi_uart_unregister_devices(struct klist *adap)
+{
+	uart_del_adapter(adap);
+	kfree(adap);
+}
+EXPORT_SYMBOL_GPL(acpi_uart_unregister_devices);
diff --git a/include/linux/acpi_uart.h b/include/linux/acpi_uart.h
new file mode 100644
index 0000000..a928425
--- /dev/null
+++ b/include/linux/acpi_uart.h
@@ -0,0 +1,40 @@
+/*
+ * acpi_uart.h - ACPI UART enumeration support
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+#ifndef _LINUX_ACPI_UART_H
+#define _LINUX_ACPI_UART_H
+
+struct uart_driver;
+
+#define ACPI_UART_LINE_UNKNOWN	((unsigned int)-1)
+
+#if IS_ENABLED(CONFIG_ACPI_UART)
+struct klist *acpi_uart_register_devices(struct klist *adap,
+					 struct device *parent,
+					 struct tty_driver *drv,
+					 unsigned int line);
+void acpi_uart_unregister_devices(struct klist *adap);
+#else
+static inline struct klist *acpi_uart_register_devices(struct klist *adap,
+						       struct device *parent,
+						       struct tty_driver *drv,
+						       unsigned int line)
+{
+	return NULL;
+}
+
+static inline void acpi_uart_unregister_devices(struct klist *adap)
+{
+}
+#endif
+
+#endif /* _LINUX_ACPI_UART_H */
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v2 3/4] UART / 8250: Add declearation of serial8250 driver.
  2012-12-05  3:51 ` [PATCH v2 0/4] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
  2012-12-05  3:51   ` [PATCH v2 1/4] UART: Add UART subsystem as a bus Lv Zheng
  2012-12-05  3:51   ` [PATCH v2 2/4] ACPI / UART: Add ACPI enumeration support for UART bus Lv Zheng
@ 2012-12-05  3:51   ` Lv Zheng
  2012-12-05  3:52   ` [PATCH v2 4/4] UART: Add dummy devices to test the enumeration Lv Zheng
  3 siblings, 0 replies; 71+ messages in thread
From: Lv Zheng @ 2012-12-05  3:51 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox,
	Mika Westerberg
  Cc: linux-acpi, linux-serial, Lv Zheng

Export serial8250 uart driver for uart bus enumerator users.

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/tty/serial/8250/8250.c |    5 ++---
 include/linux/serial_8250.h    |    2 ++
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c
index 3ba4234..af78374 100644
--- a/drivers/tty/serial/8250/8250.c
+++ b/drivers/tty/serial/8250/8250.c
@@ -56,8 +56,6 @@ static unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
 
 static unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS;
 
-static struct uart_driver serial8250_reg;
-
 static int serial_index(struct uart_port *port)
 {
 	return (serial8250_reg.minor - 64) + port->line;
@@ -2902,7 +2900,7 @@ int serial8250_find_port(struct uart_port *p)
 #define SERIAL8250_CONSOLE	NULL
 #endif
 
-static struct uart_driver serial8250_reg = {
+struct uart_driver serial8250_reg = {
 	.owner			= THIS_MODULE,
 	.driver_name		= "serial",
 	.dev_name		= "ttyS",
@@ -2910,6 +2908,7 @@ static struct uart_driver serial8250_reg = {
 	.minor			= 64,
 	.cons			= SERIAL8250_CONSOLE,
 };
+EXPORT_SYMBOL_GPL(serial8250_reg);
 
 /*
  * early_serial_setup - early registration for 8250 ports
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index c174c90..687af38 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -96,6 +96,8 @@ struct uart_8250_port {
 	void			(*dl_write)(struct uart_8250_port *, int);
 };
 
+extern struct uart_driver serial8250_reg;
+
 int serial8250_register_8250_port(struct uart_8250_port *);
 void serial8250_unregister_port(int line);
 void serial8250_suspend_port(int line);
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v2 4/4] UART: Add dummy devices to test the enumeration.
  2012-12-05  3:51 ` [PATCH v2 0/4] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
                     ` (2 preceding siblings ...)
  2012-12-05  3:51   ` [PATCH v2 3/4] UART / 8250: Add declearation of serial8250 driver Lv Zheng
@ 2012-12-05  3:52   ` Lv Zheng
  3 siblings, 0 replies; 71+ messages in thread
From: Lv Zheng @ 2012-12-05  3:52 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox,
	Mika Westerberg
  Cc: linux-acpi, linux-serial, Lv Zheng

This is a test patch that should not be merged to any of the published
Linux source tree.

1. The result of the UART dummy target device is as follows:

# udevadm monitor --kernel --environment > ~/uart.uevents
# echo add > /sys/bus/uart/uevent
# echo add > /sys/bus/uart/devices/DUMMY/uevent
# cat ~/uart.uevents
monitor will print the received events for:
KERNEL - the kernel uevent

KERNEL[252.443458] add      /bus/uart (bus)
ACTION=add
DEVPATH=/bus/uart
SEQNUM=1142
SUBSYSTEM=bus

KERNEL[268.491709] add      /devices/platform/serial8250/DUMMY (uart)
ACTION=add
DEVPATH=/devices/platform/serial8250/DUMMY
DEVTYPE=uart_device
MODALIAS=uart:DUMMY
SEQNUM=1144
SUBSYSTEM=uart

# cat /sys/bus/uart/devices/DUMMY/modalias
uart:DUMMY
# cat /sys/bus/uart/devices/DUMMY/tty_dev
ttyS3
# cat /sys/bus/uart/devices/DUMMY/tty_attrs
115200 8N1 HW SW
# cat /sys/bus/uart/devices/DUMMY/modem_lines
LE:RTS,

# ls -l /sys/bus/uart/devices
DUMMY -> ../../../devices/platform/serial8250/DUMMY
# ls -l /sys/devices/platform/serial8250/DUMMY
subsystem -> ../../../../bus/uart
host_node -> ../tty/ttyS3
# ls -l /sys/devices/platform/serial8250/tty/ttyS3
target_node -> ../../DUMMY

2. The result of the UART customized DSDT target device is as follows:

The test is based on the following customized DSDT (containing the dummy
uart host adapter INTF000 and target device INTF001):
  Device (UA00)
  {
      Name (_HID, "INTF000")  // _HID: Hardware ID
      Name (RBUF, ResourceTemplate ()
      {
          Memory32Fixed (ReadWrite,
              0x00000000,         // Address Base
              0x00001000)         // Address Length
      })
      Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
      {
          Return (RBUF)
      }
      Method (_STA, 0, NotSerialized)  // _STA: Status
      {
          Return (0x0F)
      }
      Device (BTH0)
      {
          Name (_HID, "INTF001")  // _HID: Hardware ID
          Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
          {
              Name (UBUF, ResourceTemplate ()
              {
                  UartSerialBus (0x0001C200, DataBitsEight, StopBitsOne,
                      0xC0, LittleEndian, ParityTypeNone, FlowControlHardware,
                      0x0020, 0x0020, "\\_SB.PCI0.UA00",
                      0x00, ResourceConsumer, ,
                      )
              })
              Return (UBUF)
          }
          Method (_STA, 0, NotSerialized)  // _STA: Status
          {
              Return (0x0F)
          }
      }
  }

# udevadm monitor --kernel --environment > ~/uart.uevents
# echo add > /sys/bus/uart/uevent
# echo add > /sys/bus/uart/devices/INTF001/uevent
# cat ~/uart.uevents
monitor will print the received events for:
KERNEL - the kernel uevent

KERNEL[252.443458] add      /bus/uart (bus)
ACTION=add
DEVPATH=/bus/uart
SEQNUM=1142
SUBSYSTEM=bus

KERNEL[268.491709] add      /devices/platform/INTF000:00/INTF001:00 (uart)
ACTION=add
DEVPATH=/devices/platform/INTF000:00/INTF001:00
DEVTYPE=uart_device
MODALIAS=uart:INTF001:00
SEQNUM=1144
SUBSYSTEM=uart

# cat /sys/bus/uart/devices/INTF001:00/modalias
uart:INTF001:00
# cat /sys/bus/uart/devices/INTF001:00/tty_dev
ttyS0
# cat /sys/bus/uart/devices/INTF001:00/tty_attrs
115200 8N0 HW
# cat /sys/bus/uart/devices/INTF001:00/modem_lines
LE:RTS,CTS,

# ls -l /sys/bus/uart/devices
INTF001:00 -> ../../../devices/platform/INTF000:00/INTF001:00
# ls -l /sys/devices/platform/INTF000:00/INTF001:00
subsystem -> ../../../../bus/uart
host_node -> ../tty/ttyS0
# ls -l /sys/devices/platform/INTF000:00/tty/ttyS0
target_node -> ../../INTF001:00

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/acpi/scan.c                  |    1 +
 drivers/tty/serial/8250/8250.c       |   12 ++++
 drivers/tty/serial/8250/8250_dummy.c |  129 ++++++++++++++++++++++++++++++++++
 drivers/tty/serial/8250/Kconfig      |   10 +++
 drivers/tty/serial/8250/Makefile     |    1 +
 drivers/tty/serial/serial_bus.c      |   17 +++++
 include/linux/serial_bus.h           |    5 ++
 7 files changed, 175 insertions(+)
 create mode 100644 drivers/tty/serial/8250/8250_dummy.c

diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 67a7fa6..4892015 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -36,6 +36,7 @@ static const char *dummy_hid = "device";
 static const struct acpi_device_id acpi_platform_device_ids[] = {
 
 	{ "PNP0D40" },
+	{ "INTF000" },
 
 	{ }
 };
diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c
index af78374..745e05d 100644
--- a/drivers/tty/serial/8250/8250.c
+++ b/drivers/tty/serial/8250/8250.c
@@ -35,6 +35,7 @@
 #include <linux/serial_core.h>
 #include <linux/serial.h>
 #include <linux/serial_8250.h>
+#include <linux/serial_bus.h>
 #include <linux/nmi.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
@@ -2986,6 +2987,8 @@ void serial8250_resume_port(int line)
 	uart_resume_port(&serial8250_reg, port);
 }
 
+struct uart_device *uart_dummy;
+
 /*
  * Register a set of serial devices attached to a platform device.  The
  * list is terminated with a zero flags entry, which means we expect
@@ -2996,6 +2999,7 @@ static int __devinit serial8250_probe(struct platform_device *dev)
 	struct plat_serial8250_port *p = dev->dev.platform_data;
 	struct uart_8250_port uart;
 	int ret, i, irqflag = 0;
+	unsigned int dummy_line;
 
 	memset(&uart, 0, sizeof(uart));
 
@@ -3031,6 +3035,13 @@ static int __devinit serial8250_probe(struct platform_device *dev)
 				p->irq, ret);
 		}
 	}
+
+	dummy_line = serial8250_ports[nr_uarts-1].port.line;
+	pr_info("registering DUMMY at line %d.\n", dummy_line);
+	uart_dummy = uart_register_dummy(&dev->dev,
+					 serial8250_reg.tty_driver,
+					 dummy_line);
+
 	return 0;
 }
 
@@ -3041,6 +3052,7 @@ static int __devexit serial8250_remove(struct platform_device *dev)
 {
 	int i;
 
+	uart_unregister_device(uart_dummy);
 	for (i = 0; i < nr_uarts; i++) {
 		struct uart_8250_port *up = &serial8250_ports[i];
 
diff --git a/drivers/tty/serial/8250/8250_dummy.c b/drivers/tty/serial/8250/8250_dummy.c
new file mode 100644
index 0000000..c5ec064
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_dummy.c
@@ -0,0 +1,129 @@
+/*
+ * 8250_dummy.c: Dummy 8250 UART target device enumerator
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_bus.h>
+#include <linux/acpi.h>
+#include <linux/acpi_uart.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct dummy8250_data {
+	int	last_lcr;
+	int	line;
+};
+
+static void dummy8250_serial_out(struct uart_port *p, int offset, int value)
+{
+}
+
+static unsigned int dummy8250_serial_in(struct uart_port *p, int offset)
+{
+	return 0;
+}
+
+struct klist *dummy8250_mgr;
+int dummy8250_num;
+
+static int __devinit dummy8250_probe(struct platform_device *pdev)
+{
+	struct uart_8250_port uart = {};
+	struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	struct dummy8250_data *data;
+#ifdef CONFIG_ACPI_UART
+	struct klist *mgr;
+#endif
+
+	dev_info(&pdev->dev, "1\n");
+	if (!regs) {
+		dev_err(&pdev->dev, "no registers defined\n");
+		return -EINVAL;
+	}
+
+	dev_info(&pdev->dev, "2\n");
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+	uart.port.private_data = data;
+
+	spin_lock_init(&uart.port.lock);
+	uart.port.mapbase = regs->start;
+	uart.port.type = PORT_8250;
+	uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP |
+		UPF_FIXED_PORT | UPF_FIXED_TYPE;
+	uart.port.dev = &pdev->dev;
+
+	uart.port.iotype = UPIO_MEM;
+	uart.port.serial_in = dummy8250_serial_in;
+	uart.port.serial_out = dummy8250_serial_out;
+	uart.port.uartclk = 40000000;
+
+	dev_info(&pdev->dev, "3\n");
+	data->line = serial8250_register_8250_port(&uart);
+	if (data->line < 0)
+		return data->line;
+
+#ifdef CONFIG_ACPI_UART
+	dev_info(&pdev->dev, "4\n");
+	mgr = acpi_uart_register_devices(dummy8250_mgr,
+					 &pdev->dev,
+					 serial8250_reg.tty_driver,
+					 data->line);
+	if (mgr) {
+		dummy8250_mgr = mgr;
+		dummy8250_num++;
+	}
+#endif
+	dev_info(&pdev->dev, "5\n");
+	platform_set_drvdata(pdev, data);
+
+	return 0;
+}
+
+static int __devexit dummy8250_remove(struct platform_device *pdev)
+{
+	struct dummy8250_data *data = platform_get_drvdata(pdev);
+
+#ifdef CONFIG_ACPI_UART
+	dummy8250_num--;
+	if (!dummy8250_num)
+		acpi_uart_unregister_devices(dummy8250_mgr);
+#endif
+	serial8250_unregister_port(data->line);
+
+	return 0;
+}
+
+static const struct acpi_device_id dummy8250_match[] = {
+	{ .id = "INTF000" },
+	{ /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(acpi, dummy8250_match);
+
+static struct platform_driver dummy8250_platform_driver = {
+	.driver = {
+		.name			= "dummy-uart",
+		.owner			= THIS_MODULE,
+		.acpi_match_table	= dummy8250_match,
+	},
+	.probe				= dummy8250_probe,
+	.remove				= __devexit_p(dummy8250_remove),
+};
+
+module_platform_driver(dummy8250_platform_driver);
+
+MODULE_AUTHOR("Lv Zheng");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Dummy 8250 serial port driver");
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index f3d283f..3ba480a 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -270,6 +270,16 @@ config SERIAL_8250_DW
 	  Selecting this option will enable handling of the extra features
 	  present in the Synopsys DesignWare APB UART.
 
+config SERIAL_8250_DUMMY
+	tristate "Support for dummy ACPI 8250"
+	depends on SERIAL_8250 && ACPI
+	help
+	  Selecting this option will enable a test UART target device DUMMY
+	  under the ISA serial8250 and a test UART host adapter INTF000
+	  as an platform device for the purpose of testing the ACPI UART
+	  enumeration support.
+	  If unsure, say "N" here.
+
 config SERIAL_8250_EM
 	tristate "Support for Emma Mobile intergrated serial port"
 	depends on SERIAL_8250 && ARM && HAVE_CLK
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index 108fe7f..fb82aa9 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -19,3 +19,4 @@ obj-$(CONFIG_SERIAL_8250_HUB6)		+= 8250_hub6.o
 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_DUMMY)		+= 8250_dummy.o
diff --git a/drivers/tty/serial/serial_bus.c b/drivers/tty/serial/serial_bus.c
index afbb885..d25058a 100644
--- a/drivers/tty/serial/serial_bus.c
+++ b/drivers/tty/serial/serial_bus.c
@@ -387,6 +387,23 @@ void uart_del_adapter(struct klist *adap)
 }
 EXPORT_SYMBOL_GPL(uart_del_adapter);
 
+static struct uart_board_info dummy_target = {
+	.type = "DUMMY",
+	.baud = 115200,
+	.cflag = CS8 | CSTOPB | CRTSCTS,
+	.iflag = (IXON | IXOFF),
+	.mctrl = TIOCM_LE | TIOCM_RTS,
+};
+
+struct uart_device *uart_register_dummy(struct device *dev,
+					struct tty_driver *drv,
+					unsigned int line)
+{
+	dummy_target.line = line;
+	return uart_register_device(NULL, dev, drv, &dummy_target);
+}
+EXPORT_SYMBOL_GPL(uart_register_dummy);
+
 struct bus_type uart_bus_type = {
 	.name		= "uart",
 };
diff --git a/include/linux/serial_bus.h b/include/linux/serial_bus.h
index 6d66fe2..917bbae 100644
--- a/include/linux/serial_bus.h
+++ b/include/linux/serial_bus.h
@@ -72,4 +72,9 @@ struct device *uart_tty_find(struct tty_driver *drv, unsigned int line,
 			     struct device *dev);
 void uart_tty_name(struct tty_driver *driver, unsigned int line, char *p);
 
+/* Test dummy device registration */
+struct uart_device *uart_register_dummy(struct device *dev,
+					struct tty_driver *drv,
+					unsigned int line);
+
 #endif /* LINUX_SERIAL_BUS_H */
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* Re: [RFC PATCH 1/3] UART: Add UART subsystem as a bus.
  2012-12-04 19:50     ` Alan Cox
@ 2012-12-05  6:20       ` Mika Westerberg
  2012-12-05  7:07         ` Zheng, Lv
  2012-12-05  9:43         ` Alan Cox
  0 siblings, 2 replies; 71+ messages in thread
From: Mika Westerberg @ 2012-12-05  6:20 UTC (permalink / raw)
  To: Alan Cox
  Cc: Lv Zheng, Len Brown, Rafael J Wysocki, Greg Kroah-Hartman,
	linux-acpi, linux-serial

On Tue, Dec 04, 2012 at 07:50:30PM +0000, Alan Cox wrote:
> > And if we have enumerated the UART controller from ACPI (it is
> > probably attached to the platform bus) we can find the tty device it
> > exports like:
> 
> The property should not be in any ACPI specific form or space - just
> attach it directly to the tty from ACPI, DT, driver internal knowledge,
> PCI id, whatever

The only property that comes into mind is _HID/_CID (referring to the ACPI
ID) that can be used by userspace to find out type of the device behind the
UART port. I don't know what name would be generic enough for the property,
though.

There are other resources as well in addition to the UartSerialBus(). For
example we might have two GPIO lines connected to the bluetooth chip and
these are represented as GpioIo ACPI resources.

Since the bluetooth is mostly handled by the N_HCI line discipline, should
the GPIO handling be done there as well? It can distinguish between DT and
ACPI enumerated devices by comparing dev->of_node and ACPI_HANDLE(dev) so
it can get the resources from both DT and ACPI but I'm not sure if it
really belongs there. Or should this be in a separate driver?

^ permalink raw reply	[flat|nested] 71+ messages in thread

* RE: [RFC PATCH 1/3] UART: Add UART subsystem as a bus.
  2012-12-05  6:20       ` Mika Westerberg
@ 2012-12-05  7:07         ` Zheng, Lv
  2012-12-05  7:42           ` Mika Westerberg
  2012-12-05  9:43         ` Alan Cox
  1 sibling, 1 reply; 71+ messages in thread
From: Zheng, Lv @ 2012-12-05  7:07 UTC (permalink / raw)
  To: Mika Westerberg, Alan Cox
  Cc: Brown, Len, Wysocki, Rafael J, Greg Kroah-Hartman, linux-acpi,
	linux-serial

> On Tue, Dec 04, 2012 at 07:50:30PM +0000, Alan Cox wrote:
> > > And if we have enumerated the UART controller from ACPI (it is
> > > probably attached to the platform bus) we can find the tty device it
> > > exports like:
> >
> > The property should not be in any ACPI specific form or space - just
> > attach it directly to the tty from ACPI, DT, driver internal
> > knowledge, PCI id, whatever
> 
> The only property that comes into mind is _HID/_CID (referring to the ACPI
> ID) that can be used by userspace to find out type of the device behind the
> UART port. I don't know what name would be generic enough for the property,
> though.
> 
> There are other resources as well in addition to the UartSerialBus(). For
> example we might have two GPIO lines connected to the bluetooth chip and
> these are represented as GpioIo ACPI resources.
> 
> Since the bluetooth is mostly handled by the N_HCI line discipline, should the
> GPIO handling be done there as well? It can distinguish between DT and ACPI
> enumerated devices by comparing dev->of_node and ACPI_HANDLE(dev) so it
> can get the resources from both DT and ACPI but I'm not sure if it really
> belongs there. Or should this be in a separate driver?

IMO, 
For ACPI enumerated target devices, ACPI can provide GPIO enumeration API by feeding ACPI_HANDLE(tty->target) to obtain the GPIO resources while OF can offer its own implementation.
Then there are 2 possible solutions can be found by calling such APIs:
1. implement GPIO enabling in the kernel side N_HCI proto driver.
2. implement GPIO enabling in the kernel side UART driver on TIOCSETD.

Same issues can be found for the ACPI enumerated SPI/I2C target devices.
Thus the GpioIrq and GpioIo is not handled in this patch set.

Best regards/Lv Zheng

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [RFC PATCH 1/3] UART: Add UART subsystem as a bus.
  2012-12-05  7:07         ` Zheng, Lv
@ 2012-12-05  7:42           ` Mika Westerberg
  0 siblings, 0 replies; 71+ messages in thread
From: Mika Westerberg @ 2012-12-05  7:42 UTC (permalink / raw)
  To: Zheng, Lv
  Cc: Alan Cox, Brown, Len, Wysocki, Rafael J, Greg Kroah-Hartman,
	linux-acpi, linux-serial

On Wed, Dec 05, 2012 at 07:07:52AM +0000, Zheng, Lv wrote:
> > On Tue, Dec 04, 2012 at 07:50:30PM +0000, Alan Cox wrote:
> > > > And if we have enumerated the UART controller from ACPI (it is
> > > > probably attached to the platform bus) we can find the tty device it
> > > > exports like:
> > >
> > > The property should not be in any ACPI specific form or space - just
> > > attach it directly to the tty from ACPI, DT, driver internal
> > > knowledge, PCI id, whatever
> > 
> > The only property that comes into mind is _HID/_CID (referring to the ACPI
> > ID) that can be used by userspace to find out type of the device behind the
> > UART port. I don't know what name would be generic enough for the property,
> > though.
> > 
> > There are other resources as well in addition to the UartSerialBus(). For
> > example we might have two GPIO lines connected to the bluetooth chip and
> > these are represented as GpioIo ACPI resources.
> > 
> > Since the bluetooth is mostly handled by the N_HCI line discipline, should the
> > GPIO handling be done there as well? It can distinguish between DT and ACPI
> > enumerated devices by comparing dev->of_node and ACPI_HANDLE(dev) so it
> > can get the resources from both DT and ACPI but I'm not sure if it really
> > belongs there. Or should this be in a separate driver?
> 
> IMO, 
> For ACPI enumerated target devices, ACPI can provide GPIO enumeration API
> by feeding ACPI_HANDLE(tty->target) to obtain the GPIO resources while OF
> can offer its own implementation.
> Then there are 2 possible solutions can be found by calling such APIs:
> 1. implement GPIO enabling in the kernel side N_HCI proto driver.
> 2. implement GPIO enabling in the kernel side UART driver on TIOCSETD.

OK.

> Same issues can be found for the ACPI enumerated SPI/I2C target devices.
> Thus the GpioIrq and GpioIo is not handled in this patch set.

Yeah, I wasn't expecting that this series addresses that :-) However, this
is something we need to solve at some point - we probably don't want that
userspace deals with the GPIOs.

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [RFC PATCH 1/3] UART: Add UART subsystem as a bus.
  2012-12-05  6:20       ` Mika Westerberg
  2012-12-05  7:07         ` Zheng, Lv
@ 2012-12-05  9:43         ` Alan Cox
  2012-12-06  1:26           ` Zheng, Lv
  2012-12-06  7:36           ` Zheng, Lv
  1 sibling, 2 replies; 71+ messages in thread
From: Alan Cox @ 2012-12-05  9:43 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: Alan Cox, Lv Zheng, Len Brown, Rafael J Wysocki,
	Greg Kroah-Hartman, linux-acpi, linux-serial

> > The property should not be in any ACPI specific form or space - just
> > attach it directly to the tty from ACPI, DT, driver internal knowledge,
> > PCI id, whatever
> 
> The only property that comes into mind is _HID/_CID (referring to the ACPI
> ID) that can be used by userspace to find out type of the device behind the
> UART port. I don't know what name would be generic enough for the property,
> though.

We just need a set of type names for the sysfs node I think
"bluetooth", "ups", "loconet", "serial", "modem", "cir" etc...

> There are other resources as well in addition to the UartSerialBus(). For
> example we might have two GPIO lines connected to the bluetooth chip and
> these are represented as GpioIo ACPI resources.

There was some planning on this some time ago. I think we know how to
deal with GPIOs

> Since the bluetooth is mostly handled by the N_HCI line discipline, should
> the GPIO handling be done there as well? It can distinguish between DT and
> ACPI enumerated devices by comparing dev->of_node and ACPI_HANDLE(dev) so
> it can get the resources from both DT and ACPI but I'm not sure if it
> really belongs there. Or should this be in a separate driver?

The plan was that the tty device would know its gpio lines and also
support a gpio structure giving mappings for extra control lines. It
seems we want that both visible to kernel (N_HCI etc) and visible to user
space (userspace custom handling using the gpio userspace interfaces).

Alan

^ permalink raw reply	[flat|nested] 71+ messages in thread

* RE: [RFC PATCH 1/3] UART: Add UART subsystem as a bus.
  2012-12-05  9:43         ` Alan Cox
@ 2012-12-06  1:26           ` Zheng, Lv
  2012-12-06  1:55             ` Zheng, Lv
  2012-12-06 13:53             ` Alan Cox
  2012-12-06  7:36           ` Zheng, Lv
  1 sibling, 2 replies; 71+ messages in thread
From: Zheng, Lv @ 2012-12-06  1:26 UTC (permalink / raw)
  To: Alan Cox, Mika Westerberg
  Cc: Alan Cox, Brown, Len, Wysocki, Rafael J, Greg Kroah-Hartman,
	linux-acpi, linux-serial, Heikki Krogerus, Mathias Nyman, Huang,
	Ying

> > > The property should not be in any ACPI specific form or space - just
> > > attach it directly to the tty from ACPI, DT, driver internal
> > > knowledge, PCI id, whatever
> >
> > The only property that comes into mind is _HID/_CID (referring to the
> > ACPI
> > ID) that can be used by userspace to find out type of the device
> > behind the UART port. I don't know what name would be generic enough
> > for the property, though.
> 
> We just need a set of type names for the sysfs node I think "bluetooth", "ups",
> "loconet", "serial", "modem", "cir" etc...

Is it a good idea to introduce uart_device driver in the kernel to fill a new 'ldisc' member in the uart_device or to load ldisc by default for the corresponding tty_port?

One more important thing I should mention is about a design decision for this patch set.
My current implementation is for the uart tty devices.
Shall we change the uart_bus to the tty_bus, then introduce tty_host / tty_target for the bus?
In the Linux world, tty is the only language that the "tty physical devices" will use to talk to each other, thus it make sense to have a tty_bus and enable PM ops for the physical devices on the bus.
If we do so, the tty_host or tty_port can be the functional devices on top of the host side physical tty devices (ex. platform device / pcmcia device), and the tty_target can be used instead of the uart_device.
We could leave tty_host unimplemented in the first step as there is already a tty_port abstraction.

> > There are other resources as well in addition to the UartSerialBus().
> > For example we might have two GPIO lines connected to the bluetooth
> > chip and these are represented as GpioIo ACPI resources.
> 
> There was some planning on this some time ago. I think we know how to deal
> with GPIOs
> 
> > Since the bluetooth is mostly handled by the N_HCI line discipline,
> > should the GPIO handling be done there as well? It can distinguish
> > between DT and ACPI enumerated devices by comparing dev->of_node and
> > ACPI_HANDLE(dev) so it can get the resources from both DT and ACPI but
> > I'm not sure if it really belongs there. Or should this be in a separate driver?
> 
> The plan was that the tty device would know its gpio lines and also support a
> gpio structure giving mappings for extra control lines. It seems we want that
> both visible to kernel (N_HCI etc) and visible to user space (userspace custom
> handling using the gpio userspace interfaces).

When this is ready, someone knowing ACPI GPIO better than me (Heikki or Mathias included) can add additional features on top of this.

Best regards/Lv Zheng

^ permalink raw reply	[flat|nested] 71+ messages in thread

* RE: [RFC PATCH 1/3] UART: Add UART subsystem as a bus.
  2012-12-06  1:26           ` Zheng, Lv
@ 2012-12-06  1:55             ` Zheng, Lv
  2012-12-06 13:53             ` Alan Cox
  1 sibling, 0 replies; 71+ messages in thread
From: Zheng, Lv @ 2012-12-06  1:55 UTC (permalink / raw)
  To: Zheng, Lv, Alan Cox, Mika Westerberg
  Cc: Alan Cox, Brown, Len, Wysocki, Rafael J, Greg Kroah-Hartman,
	linux-acpi, linux-serial, Heikki Krogerus, Mathias Nyman, Huang,
	Ying

> > > > The property should not be in any ACPI specific form or space -
> > > > just attach it directly to the tty from ACPI, DT, driver internal
> > > > knowledge, PCI id, whatever
> > >
> > > The only property that comes into mind is _HID/_CID (referring to
> > > the ACPI
> > > ID) that can be used by userspace to find out type of the device
> > > behind the UART port. I don't know what name would be generic enough
> > > for the property, though.
> >
> > We just need a set of type names for the sysfs node I think
> > "bluetooth", "ups", "loconet", "serial", "modem", "cir" etc...
> 
> Is it a good idea to introduce uart_device driver in the kernel to fill a new 'ldisc'
> member in the uart_device or to load ldisc by default for the corresponding
> tty_port?

In the ACPI spec, there is no such attribute for the UART devices, OF might easily introduce this.
It's hard to add type name or ldisc name directly to the uart_board_info.

We can either add driver support for the uart_device to fill this as I mentioned in the first reply or to add a HID -> type name (or ldisc name) matching table in the acpi_uart.c to extract this information for the ACPI enumerated uart_board_info.

Best regards/Lv Zheng

^ permalink raw reply	[flat|nested] 71+ messages in thread

* RE: [RFC PATCH 1/3] UART: Add UART subsystem as a bus.
  2012-12-05  9:43         ` Alan Cox
  2012-12-06  1:26           ` Zheng, Lv
@ 2012-12-06  7:36           ` Zheng, Lv
  2012-12-06  7:52             ` Mika Westerberg
  1 sibling, 1 reply; 71+ messages in thread
From: Zheng, Lv @ 2012-12-06  7:36 UTC (permalink / raw)
  To: Alan Cox, Mika Westerberg
  Cc: Alan Cox, Brown, Len, Wysocki, Rafael J, Greg Kroah-Hartman,
	linux-acpi, linux-serial

> > > The property should not be in any ACPI specific form or space - just
> > > attach it directly to the tty from ACPI, DT, driver internal
> > > knowledge, PCI id, whatever
> >
> > The only property that comes into mind is _HID/_CID (referring to the
> > ACPI
> > ID) that can be used by userspace to find out type of the device
> > behind the UART port. I don't know what name would be generic enough
> > for the property, though.
> 
> We just need a set of type names for the sysfs node I think "bluetooth", "ups",
> "loconet", "serial", "modem", "cir" etc...

Hi, Mika

How is this handled in the i2c?
I think for OF, type can be filled as "bluetooth", "ups" and etc.
But for ACPI, the type field of the i2c_board_info is filled as hid in your patches.
Is this right?

Maybe I just need to add struct acpi_dev_node to the uart_board_info and let OF guys add of_node to it.
What's your opinion?

Best regards
-Lv

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [RFC PATCH 1/3] UART: Add UART subsystem as a bus.
  2012-12-06  7:36           ` Zheng, Lv
@ 2012-12-06  7:52             ` Mika Westerberg
  0 siblings, 0 replies; 71+ messages in thread
From: Mika Westerberg @ 2012-12-06  7:52 UTC (permalink / raw)
  To: Zheng, Lv
  Cc: Alan Cox, Alan Cox, Brown, Len, Wysocki, Rafael J,
	Greg Kroah-Hartman, linux-acpi, linux-serial

On Thu, Dec 06, 2012 at 07:36:35AM +0000, Zheng, Lv wrote:
> > > > The property should not be in any ACPI specific form or space - just
> > > > attach it directly to the tty from ACPI, DT, driver internal
> > > > knowledge, PCI id, whatever
> > >
> > > The only property that comes into mind is _HID/_CID (referring to the
> > > ACPI
> > > ID) that can be used by userspace to find out type of the device
> > > behind the UART port. I don't know what name would be generic enough
> > > for the property, though.
> > 
> > We just need a set of type names for the sysfs node I think "bluetooth", "ups",
> > "loconet", "serial", "modem", "cir" etc...
> 
> Hi, Mika
> 
> How is this handled in the i2c?

We match the I2C device to the driver using _HID/_CID in the kernel. There
is no userpace involved (except maybe loading the correct driver module).

> I think for OF, type can be filled as "bluetooth", "ups" and etc.
> But for ACPI, the type field of the i2c_board_info is filled as hid in your patches.
> Is this right?

Yes.

> Maybe I just need to add struct acpi_dev_node to the uart_board_info and
> let OF guys add of_node to it.  What's your opinion?

Well, in order to use any ACPI functions and PM you need to have
ACPI_HANDLE(dev) != NULL. If the best place to pass that handle is
uart_board_info (in analogy to i2c_board_info), then yes you should add it
there.

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [RFC PATCH v3 0/4] ACPI/UART: Add ACPI 5.0 enueration support for UART.
  2012-12-03  3:39 [RFC PATCH 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
                   ` (3 preceding siblings ...)
  2012-12-05  3:51 ` [PATCH v2 0/4] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
@ 2012-12-06  9:21 ` Lv Zheng
  2012-12-06  9:21   ` [RFC PATCH v3 1/4] UART: Add UART subsystem as a bus Lv Zheng
                     ` (3 more replies)
  2013-01-09  9:17 ` [RFC PATCH v4 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
                   ` (5 subsequent siblings)
  10 siblings, 4 replies; 71+ messages in thread
From: Lv Zheng @ 2012-12-06  9:21 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox,
	Mika Westerberg
  Cc: linux-acpi, linux-serial, Lv Zheng

ACPI 5.0 specification introduces enumeration support for SPB buses. This
patch set adds the UART serial bus enumeration support to Linux using such
mechanism.

NOTE: The [PATCH 4/4] is only for the demonstration purpose and should not
      be merged into any of the published Linux source tree.

History:
v1:
  1. Add uart bus type subsystem (uart_bus).
  2. Add physical uart target device and description
     (uart_device/uart_board_info).
  3. Add logical uart host adapter (klist).
  4. Add uart target device attributes (tty_dev/tty_attrs/modem_lines).
  5. Create tty_device<->uart_device links (host_node/target_node).
v2.
  1. Change uart layer related stuff to tty layer.
  2. Modify the order of the function parameters.
v3:
  1. Add comments for the uart_board_info and the uart_device.
  2. Add platform_data/archdata support.
  3. Add ACPI binding and test the binding.
  4. Add SERIAL_BUS kconfig item.

RFCs:
  1. Do we need to rename uart_bus to tty_bus and rename uart_device to
     tty_target?
  2. Do we need a kernel driver for the uart_device?
  3. Do we need to split uart_board_info.type into uart_board_info.id and
     uart_board_info.type? How can ACPI fill the uart_board_info.type?
  4. Do we need to set line discipline for the uart_board_info?

Lv Zheng (4):
  UART: Add UART subsystem as a bus.
  ACPI / UART: Add ACPI enumeration support for UART bus.
  UART / 8250: Add declearation of serial8250 driver.
  UART: Add dummy devices to test the enumeration.

 drivers/acpi/Kconfig                 |    7 +
 drivers/acpi/Makefile                |    1 +
 drivers/acpi/acpi_uart.c             |  263 ++++++++++++++++++++
 drivers/acpi/scan.c                  |    1 +
 drivers/tty/serial/8250/8250.c       |   17 +-
 drivers/tty/serial/8250/8250_dummy.c |  129 ++++++++++
 drivers/tty/serial/8250/Kconfig      |   10 +
 drivers/tty/serial/8250/Makefile     |    1 +
 drivers/tty/serial/Kconfig           |    8 +
 drivers/tty/serial/Makefile          |    1 +
 drivers/tty/serial/serial_bus.c      |  435 ++++++++++++++++++++++++++++++++++
 include/linux/acpi_uart.h            |   40 ++++
 include/linux/mod_devicetable.h      |    5 +
 include/linux/serial_8250.h          |    2 +
 include/linux/serial_bus.h           |  126 ++++++++++
 15 files changed, 1043 insertions(+), 3 deletions(-)
 create mode 100644 drivers/acpi/acpi_uart.c
 create mode 100644 drivers/tty/serial/8250/8250_dummy.c
 create mode 100644 drivers/tty/serial/serial_bus.c
 create mode 100644 include/linux/acpi_uart.h
 create mode 100644 include/linux/serial_bus.h

-- 
1.7.10


^ permalink raw reply	[flat|nested] 71+ messages in thread

* [RFC PATCH v3 1/4] UART: Add UART subsystem as a bus.
  2012-12-06  9:21 ` [RFC PATCH v3 0/4] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
@ 2012-12-06  9:21   ` Lv Zheng
  2012-12-06 13:40     ` Alan Cox
  2012-12-06  9:22   ` [RFC PATCH v3 2/4] ACPI / UART: Add ACPI enumeration support for UART bus Lv Zheng
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 71+ messages in thread
From: Lv Zheng @ 2012-12-06  9:21 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox,
	Mika Westerberg
  Cc: linux-acpi, linux-serial, Lv Zheng

Tranditional UARTs are used as communication pipes between the hosts
while the modern computing systems equipped with HSU (High Speed UARTs)
would connect on-board target devices using the UART ports. The role of
the UART controllers is changed from the communication facility to the
platform bus.

In the recent ACPI 5.0 specification updates, firmwares are provided the
possibilities to enumerate the UART target devices known to the platform
vendors.
Thus there are the needs for enumerating the UART target devices:
1. hotplug uevent
2. serial configuration
Currently, only serial cards on the specific bus (ex. PCMCIA) can be
enumerated and userspace can obtain the hotplug event of the UART target
devices. Linux kernel is lack of an overall enumeration mechanism for
UART target devices.
In order to send uevent, a device need to be a class device or a bus
device. This patch introduces a bus_type subsystem to manage the new
UART target device type for the purpose of being ready for the possible
future extensions.
When the UART target devices are created, userspace uevent rules can
pass the creation details to the userspace driver managers
(ex. hciattach). Here is an example of the uevents and exported
attributes of these new UART target devices:

Test DSDT (dummy UART host adapter INTF000 and target device INTF001):
  Device (UA00)
  {
      Name (_HID, "INTF000")  // _HID: Hardware ID
      Name (RBUF, ResourceTemplate ()
      {
          Memory32Fixed (ReadWrite,
              0x00000000,         // Address Base
              0x00001000)         // Address Length
      })
      Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
      {
          Return (RBUF)
      }
      Method (_STA, 0, NotSerialized)  // _STA: Status
      {
          Return (0x0F)
      }
      Device (BTH0)
      {
          Name (_HID, "INTF001")  // _HID: Hardware ID
          Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
          {
              Name (UBUF, ResourceTemplate ()
              {
                  UartSerialBus (0x0001C200, DataBitsEight, StopBitsOne,
                      0xC0, LittleEndian, ParityTypeNone, FlowControlHardware,
                      0x0020, 0x0020, "\\_SB.PCI0.UA00",
                      0x00, ResourceConsumer, ,
                      )
              })
              Return (UBUF)
          }
          Method (_STA, 0, NotSerialized)  // _STA: Status
          {
              Return (0x0F)
          }
      }
  }

The test result of hotplug event monitoring:
uevent and environments:
KERNEL[252.443458] add      /bus/uart (bus)
ACTION=add
DEVPATH=/bus/uart
SEQNUM=1142
SUBSYSTEM=bus

KERNEL[268.491709] add      /devices/platform/INTF000:00/INTF001:00 (uart)
ACTION=add
DEVPATH=/devices/platform/INTF000:00/INTF001:00
DEVTYPE=uart_device
MODALIAS=uart:INTF001:00
SEQNUM=1144
SUBSYSTEM=uart

The test result of the UART target device enumeration:
kobject attribute files:
# cat /sys/bus/uart/devices/INTF001:00/modalias
uart:INTF001:00
# cat /sys/bus/uart/devices/INTF001:00/tty_dev
ttyS0
# cat /sys/bus/uart/devices/INTF001:00/tty_attrs
115200 8N0 HW
# cat /sys/bus/uart/devices/INTF001:00/modem_lines
LE:RTS,CTS,

kobject sysfs links:
# ls -l /sys/bus/uart/devices/
INTF001:00 -> ../../../devices/platform/INTF000:00/INTF001:00
# ls -l /sys/devices/platform/INTF000:00/INTF001:00/
subsystem -> ../../../../bus/uart
host_node -> ../tty/ttyS0
# ls -l /sys/devices/platform/INTF000:00/tty/ttyS0/
target_node -> ../../INTF001:00

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/tty/serial/Kconfig      |    8 +
 drivers/tty/serial/Makefile     |    1 +
 drivers/tty/serial/serial_bus.c |  417 +++++++++++++++++++++++++++++++++++++++
 include/linux/mod_devicetable.h |    5 +
 include/linux/serial_bus.h      |  119 +++++++++++
 5 files changed, 550 insertions(+)
 create mode 100644 drivers/tty/serial/serial_bus.c
 create mode 100644 include/linux/serial_bus.h

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 2a53be5..e16b03a 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -749,6 +749,14 @@ config SERIAL_HS_LPC32XX_CONSOLE
 	  ports on the LPC32XX as the console, you can do so by answering
 	  Y to this option.
 
+config SERIAL_BUS
+	bool "Enable Serial bus enumerator support"
+	help
+	  If your platform has enumeration support for the serial target
+	  devices, you can enable this option by say Y here. Such
+	  enumeration mechanisms are always provided by the firmwares.
+	  If unsure, say N.
+
 config SERIAL_CORE
 	tristate
 
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 4f694da..9a05bbc 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -3,6 +3,7 @@
 #
 
 obj-$(CONFIG_SERIAL_CORE) += serial_core.o
+obj-$(CONFIG_SERIAL_BUS) += serial_bus.o
 obj-$(CONFIG_SERIAL_21285) += 21285.o
 
 # These Sparc drivers have to appear before others such as 8250
diff --git a/drivers/tty/serial/serial_bus.c b/drivers/tty/serial/serial_bus.c
new file mode 100644
index 0000000..ddb5341
--- /dev/null
+++ b/drivers/tty/serial/serial_bus.c
@@ -0,0 +1,417 @@
+/*
+ * serial_bus.c - UART bus implementation
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+/*
+ * Tranditional UARTs are used as communication pipes between the hosts
+ * while the modern computing systems equipped with HSU (High Speed UARTs)
+ * would connect on-board target devices using the UART ports. The role of
+ * the UART controllers are changed from the communication facility to the
+ * platform bus.
+ *
+ * UART target devices are created in the kernel as struct uart_device.
+ * It is defined for the following purposes:
+ * 1. Sending hotplug notifications to the userspace
+ * 2. Exporting serial configuration parameters to the userspace
+ * 3. Allowing target device based PM to be added easily
+ *
+ */
+
+#include <linux/serial.h>
+#include <linux/serial_bus.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+
+struct uart_match {
+	dev_t devt;
+};
+
+static int do_uart_tty_find(struct device *dev, void *data)
+{
+	struct uart_match *match = data;
+	return dev->devt == match->devt;
+}
+
+/**
+ * uart_tty_find - find the tty device using the line number
+ * @drv: the tty host driver
+ * @line: the line number of the tty device
+ * @dev: the physical uart host adapter
+ *
+ * Return a matching tty device on the host adapter.
+ */
+struct device *uart_tty_find(struct tty_driver *drv, unsigned int line,
+			     struct device *dev)
+{
+	struct uart_match match;
+
+	match.devt = MKDEV(drv->major, drv->minor_start + line);
+
+	return device_find_child(dev, &match, do_uart_tty_find);
+}
+EXPORT_SYMBOL_GPL(uart_tty_find);
+
+/**
+ * uart_tty_name - get the name of the tty device according to the line
+ *                 number
+ * @drv: the tty host driver
+ * @line: the line number of the tty device
+ * @p: pointer to the buffer containing the returned name
+ *
+ * Return the name of the tty device.
+ */
+void uart_tty_name(struct tty_driver *drv, unsigned int line, char *p)
+{
+	BUG_ON(!drv || !p);
+
+	if (drv->flags & TTY_DRIVER_UNNUMBERED_NODE)
+		strcpy(p, drv->name);
+	else
+		sprintf(p, "%s%d", drv->name, line + drv->name_base);
+}
+EXPORT_SYMBOL_GPL(uart_tty_name);
+
+static ssize_t uart_dev_show_name(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct uart_device *udev = to_uart_device(dev);
+	return sprintf(buf, "%s\n", udev->name);
+}
+
+static ssize_t uart_dev_show_modalias(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct uart_device *udev = to_uart_device(dev);
+	return sprintf(buf, "%s%s\n", UART_MODULE_PREFIX, udev->name);
+}
+
+static ssize_t uart_dev_show_tty_dev(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct uart_device *udev = to_uart_device(dev);
+	return sprintf(buf, "%s\n", udev->tty_name);
+}
+
+static ssize_t uart_dev_show_tty_attrs(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct uart_device *udev = to_uart_device(dev);
+	int len = 0;
+
+	/* baud rate */
+	len += sprintf(buf+len, "%d ", udev->baud);
+
+	/* data bits */
+	switch (udev->cflag & CSIZE) {
+	case CS5:
+		len += sprintf(buf+len, "5");
+		break;
+	case CS6:
+		len += sprintf(buf+len, "6");
+		break;
+	case CS7:
+		len += sprintf(buf+len, "7");
+		break;
+	case CS8:
+	default:
+		len += sprintf(buf+len, "8");
+		break;
+	}
+
+	/* parity */
+	if (udev->cflag & PARODD)
+		len += sprintf(buf+len, "O");
+	else if (udev->cflag & PARENB)
+		len += sprintf(buf+len, "E");
+	else
+		len += sprintf(buf+len, "N");
+
+	/* stop bits */
+	len += sprintf(buf+len, "%d", udev->cflag & CSTOPB ? 1 : 0);
+
+	/* HW/SW control */
+	if (udev->cflag & CRTSCTS)
+		len += sprintf(buf+len, " HW");
+	if ((udev->iflag & (IXON|IXOFF|IXANY)) != 0)
+		len += sprintf(buf+len, " SW");
+
+	len += sprintf(buf+len, "\n");
+	return len;
+}
+
+static ssize_t uart_dev_show_modem_lines(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct uart_device *udev = to_uart_device(dev);
+	int len = 0;
+
+	/* endian */
+	if (udev->mctrl & TIOCM_LE)
+		len += sprintf(buf+len, "LE:");
+	else
+		len += sprintf(buf+len, "BE:");
+
+	/* terminal lines */
+	if (udev->mctrl & TIOCM_DTR)
+		len += sprintf(buf+len, "DTR,");
+	if (udev->mctrl & TIOCM_RTS)
+		len += sprintf(buf+len, "RTS,");
+
+	/* modem lines */
+	if (udev->mctrl & TIOCM_CTS)
+		len += sprintf(buf+len, "CTS,");
+	if (udev->mctrl & TIOCM_CAR)
+		len += sprintf(buf+len, "CAR,");
+	if (udev->mctrl & TIOCM_RNG)
+		len += sprintf(buf+len, "RNG,");
+	if (udev->mctrl & TIOCM_DSR)
+		len += sprintf(buf+len, "DSR,");
+
+	len += sprintf(buf+len, "\n");
+	return len;
+}
+
+static DEVICE_ATTR(name, S_IRUGO, uart_dev_show_name, NULL);
+static DEVICE_ATTR(modalias, S_IRUGO, uart_dev_show_modalias, NULL);
+static DEVICE_ATTR(tty_dev, S_IRUGO, uart_dev_show_tty_dev, NULL);
+static DEVICE_ATTR(tty_attrs, S_IRUGO, uart_dev_show_tty_attrs, NULL);
+static DEVICE_ATTR(modem_lines, S_IRUGO, uart_dev_show_modem_lines, NULL);
+
+static struct attribute *uart_dev_attrs[] = {
+	&dev_attr_name.attr,
+	/* coldplug: modprobe $(cat .../modalias) */
+	&dev_attr_modalias.attr,
+	&dev_attr_tty_dev.attr,
+	&dev_attr_tty_attrs.attr,
+	&dev_attr_modem_lines.attr,
+	NULL,
+};
+
+static struct attribute_group uart_dev_attr_group = {
+	.attrs	= uart_dev_attrs,
+};
+
+static const struct attribute_group *uart_dev_attr_groups[] = {
+	&uart_dev_attr_group,
+	NULL,
+};
+
+#ifdef CONFIG_HOTPLUG
+static int uart_device_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct uart_device *udev = to_uart_device(dev);
+
+	if (add_uevent_var(env, "MODALIAS=%s%s",
+			   UART_MODULE_PREFIX, udev->name))
+		return -ENOMEM;
+
+	dev_dbg(dev, "uevent\n");
+	return 0;
+}
+#else
+#define uart_device_uevent	NULL
+#endif
+
+static void uart_device_release(struct device *dev)
+{
+	struct uart_device *udev = to_uart_device(dev);
+
+	put_device(udev->tty);
+	kfree(udev);
+}
+
+struct device_type uart_device_type = {
+	.name		= "uart_device",
+	.groups		= uart_dev_attr_groups,
+	.uevent		= uart_device_uevent,
+	.release	= uart_device_release,
+};
+EXPORT_SYMBOL_GPL(uart_device_type);
+
+/**
+ * uart_register_device - register a physical uart target device
+ * @adap: the logical uart host adapter
+ * @dev: the physical uart host adapter
+ * @drv: the tty host driver
+ * @info: the uart target device description
+ *
+ * Initialize and add a physical uart target device.
+ *
+ * This returns the new uart target device, which may be saved for later use
+ * with uart_unregister_device; or NULL to indicate an error.
+ */
+struct uart_device *uart_register_device(struct klist *adap,
+					 struct device *dev,
+					 struct tty_driver *drv,
+					 struct uart_board_info const *info)
+{
+	struct uart_device *udev;
+	struct device *tty;
+	int status;
+
+	BUG_ON((!adap && !dev) || !drv);
+
+	udev = kzalloc(sizeof(struct uart_device), GFP_KERNEL);
+	if (!udev)
+		return NULL;
+
+	udev->dev.platform_data = info->platform_data;
+	if (info->archdata)
+		udev->dev.archdata = *info->archdata;
+
+	udev->baud = info->baud;
+	udev->cflag = info->cflag;
+	udev->iflag = info->iflag;
+	udev->mctrl = info->mctrl;
+	udev->irq = info->irq;
+
+	strlcpy(udev->name, info->type, sizeof(udev->name));
+
+	udev->dev.parent = dev;
+	udev->dev.bus = &uart_bus_type;
+	udev->dev.type = &uart_device_type;
+
+	tty = uart_tty_find(drv, info->line, dev);
+	if (!tty) {
+		dev_err(dev, "Cannot find associated tty device at line %d.\n",
+			info->line);
+		goto fail;
+	}
+	udev->tty = get_device(tty);
+	uart_tty_name(drv, info->line, udev->tty_name);
+
+	dev_set_name(&udev->dev, "%s", udev->name);
+	status = device_register(&udev->dev);
+	if (status) {
+		dev_err(dev,
+			"Failed to register uart target device at line %d.\n",
+			info->line);
+		goto fail2;
+	}
+	dev_info(&udev->dev, "Registered at line %d.\n", info->line);
+
+	status = sysfs_create_link(&tty->kobj, &udev->dev.kobj, "target_node");
+	status = sysfs_create_link(&udev->dev.kobj, &tty->kobj, "host_node");
+
+	if (adap) {
+		udev->adap = adap;
+		klist_add_tail(&udev->klist_parent, adap);
+	}
+
+	return udev;
+
+fail2:
+	put_device(tty);
+fail:
+	kfree(udev);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(uart_register_device);
+
+/**
+ * uart_unregister_device - unregister a physical uart target device
+ * @udev: the physical uart target device
+ *
+ * Reverse effect of uart_register_device().
+ */
+void uart_unregister_device(struct uart_device *udev)
+{
+	if (!udev)
+		return;
+	dev_info(&udev->dev, "Unregistering...\n");
+
+	if (udev->adap)
+		klist_del(&udev->klist_parent);
+	sysfs_remove_link(&udev->dev.kobj, "host_node");
+	if (udev->tty)
+		sysfs_remove_link(&udev->tty->kobj, "target_node");
+	device_unregister(&udev->dev);
+}
+EXPORT_SYMBOL_GPL(uart_unregister_device);
+
+static void klist_uart_get(struct klist_node *n)
+{
+	struct uart_device *udev = uart_device_from_parent(n);
+	get_device(&udev->dev);
+}
+
+static void klist_uart_put(struct klist_node *n)
+{
+	struct uart_device *udev = uart_device_from_parent(n);
+	put_device(&udev->dev);
+}
+
+static struct uart_device *uart_next_device(struct klist_iter *i)
+{
+	struct klist_node *n = klist_next(i);
+	return n ? uart_device_from_parent(n) : NULL;
+}
+
+/**
+ * uart_add_adapter - register a logical uart host adapter
+ * @adap: the logical uart host adapter
+ *
+ * Initialize a logical uart host adapter.
+ */
+int uart_add_adapter(struct klist *adap)
+{
+	klist_init(adap, klist_uart_get, klist_uart_put);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(uart_add_adapter);
+
+/**
+ * uart_del_adapter - unregister a logical uart host adapter
+ * @adap: the logical uart host adapter
+ *
+ * Reverse effect of uart_add_adapter().
+ */
+void uart_del_adapter(struct klist *adap)
+{
+	struct klist_iter i;
+	struct uart_device *udev;
+
+	if (!adap)
+		return;
+
+	klist_iter_init(adap, &i);
+	while ((udev = uart_next_device(&i)))
+		uart_unregister_device(udev);
+	klist_iter_exit(&i);
+}
+EXPORT_SYMBOL_GPL(uart_del_adapter);
+
+struct bus_type uart_bus_type = {
+	.name		= "uart",
+};
+EXPORT_SYMBOL_GPL(uart_bus_type);
+
+static int __init uart_bus_init(void)
+{
+	int retval;
+
+	retval = bus_register(&uart_bus_type);
+	if (retval)
+		return retval;
+
+	return 0;
+}
+
+static void __exit uart_bus_exit(void)
+{
+	bus_unregister(&uart_bus_type);
+}
+
+subsys_initcall(uart_bus_init);
+module_exit(uart_bus_exit);
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index fed3def..28df140 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -455,6 +455,11 @@ struct spi_device_id {
 			__attribute__((aligned(sizeof(kernel_ulong_t))));
 };
 
+/* uart */
+
+#define UART_NAME_SIZE	32
+#define UART_MODULE_PREFIX "uart:"
+
 /* dmi */
 enum dmi_field {
 	DMI_NONE,
diff --git a/include/linux/serial_bus.h b/include/linux/serial_bus.h
new file mode 100644
index 0000000..d8b56ae
--- /dev/null
+++ b/include/linux/serial_bus.h
@@ -0,0 +1,119 @@
+/*
+ * serial_bus.h - UART bus implementation
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+#ifndef LINUX_SERIAL_BUS_H
+#define LINUX_SERIAL_BUS_H
+
+#include <linux/compiler.h>
+#include <linux/tty.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+
+struct uart_device;
+
+/**
+ * struct uart_board_info - template for device creation
+ * @type: chip type, to initialize uart_devide.name
+ * @line: line number, to find associated tty device
+ * @cflag: termio cflag, preferred termio cflag to be used to communicate
+ *         with this target device
+ * @iflag: termio iflag, preferred termio iflag to be used to communicate
+ *         with this target device
+ * @mctrl: termio mctrl, preferred termio mctrl to be used to communicate
+ *         with this target device
+ * @baud: termio baud, preferred termio baud rate to be used to communicate
+ *        with this target device
+ * @irq: stored in uart_device.irq
+ * @platform_data: stored in uart_device.dev.platform_data
+ * @archdata: copied into uart_device.dev.archdata
+ *
+ * uart_board_info is used to build tables of information listing UART
+ * devices that are present. This information is used to grow the driver
+ * model tree. For add-on boards, uart_register_device() does this
+ * dynamically with the host side physical device already known.
+ */
+struct uart_board_info {
+	char type[UART_NAME_SIZE];
+	unsigned int line;	/* port index */
+	unsigned int cflag;	/* termio cflag */
+	unsigned int iflag;	/* termio iflag */
+	unsigned int mctrl;	/* modem ctrl settings */
+	unsigned int baud;
+	int irq;
+	void *platform_data;
+	struct dev_archdata *archdata;
+};
+
+extern struct bus_type uart_bus_type;
+
+int uart_add_adapter(struct klist *adap);
+void uart_del_adapter(struct klist *adap);
+
+/**
+ * struct uart_device - represent a UART target device
+ * @name: Indicates the type of the device, usually a chip name that's
+ *        generic enough to hide second-sourcing and compatible revisions
+ * @tty_name: associated tty device name
+ * @cflag: preferred termio cflag used to communicate with this target
+ *         device
+ * @iflag: preferred termio iflag used to communicate with this target
+ *         device
+ * @mctrl: preferred termio mctrl used to communicate with this target
+ *         device
+ * @baud: preferred termio baud rate used to communicate with this target
+ *        device
+ * @irq: indicates the IRQ generated by this target device (if any)
+ * @dev: driver model device node for the target device
+ * @tty: associated tty device
+ * @klist_parent: link to the logical adapter device that manages this
+ *                target device
+ * @adap: logical adapter device that manages this target device
+ *
+ * A uart_device identifies a single device (i.e. chip) connected to a
+ * uart port.
+ */
+struct uart_device {
+	char			name[UART_NAME_SIZE];
+	char			tty_name[64];
+	unsigned int		cflag;	/* termio cflag */
+	unsigned int		iflag;	/* termio iflag */
+	unsigned int		mctrl;	/* modem ctrl settings */
+	unsigned int		baud;
+	struct device		dev;
+	int			irq;	/* irq issued by device */
+	struct device		*tty;
+	struct klist_node	klist_parent;
+	struct klist		*adap;	/* set for multi-port adapter */
+};
+
+extern struct device_type uart_device_type;
+
+#define is_uart_device(d) ((d) && (d)->type == &uart_device_type)
+#define to_uart_device(d) container_of(d, struct uart_device, dev)
+#define uart_device_from_parent(n)	\
+	container_of(n, struct uart_device, klist_parent)
+
+static inline struct uart_device *uart_verify_device(struct device *dev)
+{
+	return is_uart_device(dev) ? to_uart_device(dev) : NULL;
+}
+
+struct uart_device *uart_register_device(struct klist *adap,
+					 struct device *dev,
+					 struct tty_driver *drv,
+					 struct uart_board_info const *info);
+void uart_unregister_device(struct uart_device *udev);
+
+struct device *uart_tty_find(struct tty_driver *drv, unsigned int line,
+			     struct device *dev);
+void uart_tty_name(struct tty_driver *driver, unsigned int line, char *p);
+
+#endif /* LINUX_SERIAL_BUS_H */
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [RFC PATCH v3 2/4] ACPI / UART: Add ACPI enumeration support for UART bus.
  2012-12-06  9:21 ` [RFC PATCH v3 0/4] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
  2012-12-06  9:21   ` [RFC PATCH v3 1/4] UART: Add UART subsystem as a bus Lv Zheng
@ 2012-12-06  9:22   ` Lv Zheng
  2012-12-06  9:22   ` [RFC PATCH v3 3/4] UART / 8250: Add declearation of serial8250 driver Lv Zheng
  2012-12-06  9:22   ` [RFC PATCH v3 4/4] UART: Add dummy devices to test the enumeration Lv Zheng
  3 siblings, 0 replies; 71+ messages in thread
From: Lv Zheng @ 2012-12-06  9:22 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox,
	Mika Westerberg
  Cc: linux-acpi, linux-serial, Lv Zheng

ACPI 5.0 specification introduces the methods of enumerating the target
devices connected on the serial buses.
This patch follows the specification, implementing such UART enumeration
machanism for the Linux.

In order to use this UART device enumeration mechanism, driver writers
are required to call the following APIs:
1. APIs called _after_ the creation of the uart ports:
   adap = acpi_uart_register_devices(adap, parent, driver, line);
   Where:
    driver: the low level UART driver
    parent: the physical device of the UART ports
    adap: the management list of the target devices, can be set as NULL
    line: the line number of the target device
2. APIs called _before_ the deletion of the uart ports:
   acpi_uart_unregister_devices(adap);
   Where:
    adap: the management list of the UART target devices

NOTE: If the driver writer has already created the management list for
      the UART target devices, the adap parameter can be set to the
      already created non-NULL value.
NOTE: The ACPI 5.0 specification assumes one physical device per-port.
      In this situation, the line parameter might be set to 0 when the
      acpi_uart_register_devices() is called.
      This patch set can also support the multi-port UART adapters,
      where the line should be set as ACPI_UART_LINE_UNKNOWN.

The test result of ACPI bindings:
kobject sysfs links:
# ls -l /sys/bus/acpi/INTF001:00/
physical_node -> ../../../../pnp0/00:00
physical_node1 -> ../../../../platform/INTF000:00/INTF001:00
# ls -l /sys/devices/platform/INTF000:00/INTF001:00/
firmware_node -> ../../../LNXSYSTM:00/device:00/INTF000:00/INTF001:00

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/acpi/Kconfig            |    7 ++
 drivers/acpi/Makefile           |    1 +
 drivers/acpi/acpi_uart.c        |  263 +++++++++++++++++++++++++++++++++++++++
 drivers/tty/serial/serial_bus.c |    1 +
 include/linux/acpi_uart.h       |   40 ++++++
 include/linux/serial_bus.h      |    2 +
 6 files changed, 314 insertions(+)
 create mode 100644 drivers/acpi/acpi_uart.c
 create mode 100644 include/linux/acpi_uart.h

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 0300bf6..404df82 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -187,6 +187,12 @@ config ACPI_I2C
 	help
 	  ACPI I2C enumeration support.
 
+config ACPI_UART
+	def_tristate SERIAL_BUS
+	depends on SERIAL_BUS
+	help
+	  ACPI UART enumeration support.
+
 config ACPI_PROCESSOR
 	tristate "Processor"
 	select THERMAL
@@ -200,6 +206,7 @@ config ACPI_PROCESSOR
 
 	  To compile this driver as a module, choose M here:
 	  the module will be called processor.
+
 config ACPI_IPMI
 	tristate "IPMI"
 	depends on EXPERIMENTAL && IPMI_SI && IPMI_HANDLER
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 2a4502b..784f332 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -71,6 +71,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS)	+= ec_sys.o
 obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
 obj-$(CONFIG_ACPI_BGRT)		+= bgrt.o
 obj-$(CONFIG_ACPI_I2C)		+= acpi_i2c.o
+obj-$(CONFIG_ACPI_UART)		+= acpi_uart.o
 
 # processor has its own "processor." module_param namespace
 processor-y			:= processor_driver.o processor_throttling.o
diff --git a/drivers/acpi/acpi_uart.c b/drivers/acpi/acpi_uart.c
new file mode 100644
index 0000000..bfc584f
--- /dev/null
+++ b/drivers/acpi/acpi_uart.c
@@ -0,0 +1,263 @@
+/*
+ * acpi_uart.c - ACPI UART enumeration support
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+#include <linux/init.h>
+#include <linux/serial_bus.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/acpi_uart.h>
+#include <acpi/acpi.h>
+#include <acpi/acpi_bus.h>
+
+
+static int
+acpi_uart_add_resources(struct acpi_resource *ares, void *context)
+{
+	struct uart_board_info *info = context;
+
+	if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
+		struct acpi_resource_uart_serialbus *sb;
+
+		sb = &ares->data.uart_serial_bus;
+		if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_UART)
+			return 1;
+
+		/* baud rate */
+		info->baud = sb->default_baud_rate;
+
+		/* data bits */
+		info->cflag &= ~CSIZE;
+		switch (sb->data_bits) {
+		case ACPI_UART_5_DATA_BITS:
+			info->cflag |= CS5;
+			break;
+		case ACPI_UART_6_DATA_BITS:
+			info->cflag |= CS6;
+			break;
+		case ACPI_UART_7_DATA_BITS:
+			info->cflag |= CS7;
+			break;
+		case ACPI_UART_8_DATA_BITS:
+		default:
+			info->cflag |= CS8;
+			break;
+		}
+
+		/* parity */
+		info->cflag &= ~(PARENB | PARODD);
+		if (sb->parity == ACPI_UART_PARITY_EVEN)
+			info->cflag |= PARENB;
+		else if (sb->parity == ACPI_UART_PARITY_ODD)
+			info->cflag |= (PARENB | PARODD);
+
+		/* stop bits */
+		if (sb->stop_bits == ACPI_UART_2_STOP_BITS)
+			info->cflag |= CSTOPB;
+		else
+			info->cflag &= ~CSTOPB;
+
+		/* HW control */
+		if (sb->flow_control & ACPI_UART_FLOW_CONTROL_HW)
+			info->cflag |= CRTSCTS;
+		else
+			info->cflag &= ~CRTSCTS;
+
+		/* SW control */
+		if (sb->flow_control & ACPI_UART_FLOW_CONTROL_XON_XOFF)
+			info->iflag |= (IXON | IXOFF);
+		else
+			info->iflag &= ~(IXON|IXOFF|IXANY);
+
+		/* endianess */
+		if (sb->endian == ACPI_UART_LITTLE_ENDIAN)
+			info->mctrl |= TIOCM_LE;
+		else
+			info->mctrl &= ~TIOCM_LE;
+
+		/* terminal lines */
+		if (sb->lines_enabled & ACPI_UART_DATA_TERMINAL_READY)
+			info->mctrl |= TIOCM_DTR;
+		else
+			info->mctrl &= ~TIOCM_DTR;
+		if (sb->lines_enabled & ACPI_UART_REQUEST_TO_SEND)
+			info->mctrl |= TIOCM_RTS;
+		else
+			info->mctrl &= ~TIOCM_RTS;
+
+		/* modem lines */
+		if (sb->lines_enabled & ACPI_UART_CLEAR_TO_SEND)
+			info->mctrl |= TIOCM_CTS;
+		else
+			info->mctrl &= ~TIOCM_CTS;
+		if (sb->lines_enabled & ACPI_UART_CARRIER_DETECT)
+			info->mctrl |= TIOCM_CAR;
+		else
+			info->mctrl &= ~TIOCM_CAR;
+		if (sb->lines_enabled & ACPI_UART_RING_INDICATOR)
+			info->mctrl |= TIOCM_RNG;
+		else
+			info->mctrl &= ~TIOCM_RNG;
+		if (sb->lines_enabled & ACPI_UART_DATA_SET_READY)
+			info->mctrl |= TIOCM_DSR;
+		else
+			info->mctrl &= ~TIOCM_DSR;
+	} else if (info->irq < 0) {
+		struct resource r;
+
+		if (acpi_dev_resource_interrupt(ares, 0, &r))
+			info->irq = r.start;
+	}
+
+	return 1;
+}
+
+struct acpi_uart_walk {
+	struct tty_driver *drv;
+	unsigned int line;
+	struct device *parent;
+};
+
+static acpi_status acpi_uart_add_device(acpi_handle handle, u32 level,
+					void *context, void **return_value)
+{
+	struct acpi_uart_walk *walk = context;
+	struct tty_driver *drv = walk->drv;
+	unsigned int line = walk->line;
+	struct device *parent = walk->parent;
+	struct acpi_device_info *info;
+	struct uart_board_info board_info;
+	struct acpi_device *adev;
+	struct list_head resource_list;
+	int ret;
+	acpi_status status;
+	struct klist *adap = *return_value;
+	struct uart_device *udev;
+
+	BUG_ON(!parent || !adap || !drv);
+
+	/*
+	 * Current BIOS will create physical adapter DEVICE per port.
+	 * This implementation assumes multi-port DEVICE will use _ADR
+	 * as serial port line number though there is no such explicit
+	 * proof for this assumption in the ACPI specification.
+	 *
+	 * NOTE: There is no real BIOS implementation using _ADR as line
+	 *       number.
+	 */
+	if (line == ACPI_UART_LINE_UNKNOWN) {
+		status = acpi_get_object_info(handle, &info);
+		if (ACPI_FAILURE(status))
+			return AE_OK;
+		if (info->valid & ACPI_VALID_ADR)
+			line = info->address;
+	}
+
+	if (line == ACPI_UART_LINE_UNKNOWN)
+		return AE_OK;
+	if (acpi_bus_get_device(handle, &adev))
+		return AE_OK;
+	if (acpi_bus_get_status(adev) || !adev->status.present)
+		return AE_OK;
+
+	memset(&board_info, 0, sizeof(board_info));
+	board_info.acpi_node.handle = handle;
+	board_info.irq = -1;
+	board_info.line = line;
+
+	INIT_LIST_HEAD(&resource_list);
+	ret = acpi_dev_get_resources(adev, &resource_list,
+				     acpi_uart_add_resources, &board_info);
+	acpi_dev_free_resource_list(&resource_list);
+
+	if (ret < 0 || !board_info.baud)
+		return AE_OK;
+
+	strlcpy(board_info.type, dev_name(&adev->dev), sizeof(board_info.type));
+
+	udev = uart_register_device(adap, parent, drv, &board_info);
+	if (!udev) {
+		dev_err(&adev->dev, "failed to add #%d uart device from ACPI\n",
+			line);
+		return AE_OK;
+	}
+
+	return AE_OK;
+}
+
+/**
+ * acpi_uart_register_devices - register the uart slave devices behind the
+ *                              uart host adapter
+ * @adap: the logical uart host adapter
+ * @dev: the physical uart host adapter
+ * @drv: the tty host driver
+ * @line: the serial line number
+ *
+ * Enumerate all uart slave devices behind the adapter by walking the ACPI
+ * namespace. When a device is found, it will be added to the Linux device
+ * model and bound to the corresponding ACPI handle.
+ * If the physical device is a multiple port device, line should be -1.
+ * If the physical device is a single port device, line should be the line
+ * number.
+ */
+struct klist *acpi_uart_register_devices(struct klist *adap,
+					 struct device *parent,
+					 struct tty_driver *drv,
+					 unsigned int line)
+{
+	acpi_handle handle;
+	acpi_status status;
+	struct klist *klist = NULL;
+	struct acpi_uart_walk walk = {drv, line, parent};
+
+	BUG_ON(!drv || !parent);
+
+	handle = ACPI_HANDLE(parent);
+	if (!handle)
+		return NULL;
+
+	if (!adap) {
+		klist = kzalloc(sizeof(struct klist), GFP_KERNEL);
+		if (!klist)
+			return NULL;
+		(void)uart_add_adapter(klist);
+		adap = klist;
+	}
+
+	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
+				     acpi_uart_add_device, NULL, &walk,
+				     (void **)&adap);
+	if (ACPI_FAILURE(status)) {
+		dev_warn(parent, "failed to enumerate UART slaves\n");
+		goto fail;
+	}
+
+	return adap;
+
+fail:
+	uart_del_adapter(klist);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(acpi_uart_register_devices);
+
+/**
+ * acpi_uart_register_devices - unregister the uart slave devices behind the
+ *                              uart host adapter
+ * @adap: the logical uart host adapter
+ *
+ * Reverse effect of acpi_uart_register_devices().
+ */
+void acpi_uart_unregister_devices(struct klist *adap)
+{
+	uart_del_adapter(adap);
+	kfree(adap);
+}
+EXPORT_SYMBOL_GPL(acpi_uart_unregister_devices);
diff --git a/drivers/tty/serial/serial_bus.c b/drivers/tty/serial/serial_bus.c
index ddb5341..66c112d 100644
--- a/drivers/tty/serial/serial_bus.c
+++ b/drivers/tty/serial/serial_bus.c
@@ -281,6 +281,7 @@ struct uart_device *uart_register_device(struct klist *adap,
 	udev->dev.parent = dev;
 	udev->dev.bus = &uart_bus_type;
 	udev->dev.type = &uart_device_type;
+	ACPI_HANDLE_SET(&udev->dev, info->acpi_node.handle);
 
 	tty = uart_tty_find(drv, info->line, dev);
 	if (!tty) {
diff --git a/include/linux/acpi_uart.h b/include/linux/acpi_uart.h
new file mode 100644
index 0000000..a928425
--- /dev/null
+++ b/include/linux/acpi_uart.h
@@ -0,0 +1,40 @@
+/*
+ * acpi_uart.h - ACPI UART enumeration support
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+#ifndef _LINUX_ACPI_UART_H
+#define _LINUX_ACPI_UART_H
+
+struct uart_driver;
+
+#define ACPI_UART_LINE_UNKNOWN	((unsigned int)-1)
+
+#if IS_ENABLED(CONFIG_ACPI_UART)
+struct klist *acpi_uart_register_devices(struct klist *adap,
+					 struct device *parent,
+					 struct tty_driver *drv,
+					 unsigned int line);
+void acpi_uart_unregister_devices(struct klist *adap);
+#else
+static inline struct klist *acpi_uart_register_devices(struct klist *adap,
+						       struct device *parent,
+						       struct tty_driver *drv,
+						       unsigned int line)
+{
+	return NULL;
+}
+
+static inline void acpi_uart_unregister_devices(struct klist *adap)
+{
+}
+#endif
+
+#endif /* _LINUX_ACPI_UART_H */
diff --git a/include/linux/serial_bus.h b/include/linux/serial_bus.h
index d8b56ae..1955d3e 100644
--- a/include/linux/serial_bus.h
+++ b/include/linux/serial_bus.h
@@ -34,6 +34,7 @@ struct uart_device;
  * @irq: stored in uart_device.irq
  * @platform_data: stored in uart_device.dev.platform_data
  * @archdata: copied into uart_device.dev.archdata
+ * @acpi_node: ACPI device node
  *
  * uart_board_info is used to build tables of information listing UART
  * devices that are present. This information is used to grow the driver
@@ -50,6 +51,7 @@ struct uart_board_info {
 	int irq;
 	void *platform_data;
 	struct dev_archdata *archdata;
+	struct acpi_dev_node acpi_node;
 };
 
 extern struct bus_type uart_bus_type;
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [RFC PATCH v3 3/4] UART / 8250: Add declearation of serial8250 driver.
  2012-12-06  9:21 ` [RFC PATCH v3 0/4] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
  2012-12-06  9:21   ` [RFC PATCH v3 1/4] UART: Add UART subsystem as a bus Lv Zheng
  2012-12-06  9:22   ` [RFC PATCH v3 2/4] ACPI / UART: Add ACPI enumeration support for UART bus Lv Zheng
@ 2012-12-06  9:22   ` Lv Zheng
  2012-12-06  9:22   ` [RFC PATCH v3 4/4] UART: Add dummy devices to test the enumeration Lv Zheng
  3 siblings, 0 replies; 71+ messages in thread
From: Lv Zheng @ 2012-12-06  9:22 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox,
	Mika Westerberg
  Cc: linux-acpi, linux-serial, Lv Zheng

Export serial8250 uart driver for uart bus enumerator users.

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/tty/serial/8250/8250.c |    5 ++---
 include/linux/serial_8250.h    |    2 ++
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c
index 3ba4234..af78374 100644
--- a/drivers/tty/serial/8250/8250.c
+++ b/drivers/tty/serial/8250/8250.c
@@ -56,8 +56,6 @@ static unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
 
 static unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS;
 
-static struct uart_driver serial8250_reg;
-
 static int serial_index(struct uart_port *port)
 {
 	return (serial8250_reg.minor - 64) + port->line;
@@ -2902,7 +2900,7 @@ int serial8250_find_port(struct uart_port *p)
 #define SERIAL8250_CONSOLE	NULL
 #endif
 
-static struct uart_driver serial8250_reg = {
+struct uart_driver serial8250_reg = {
 	.owner			= THIS_MODULE,
 	.driver_name		= "serial",
 	.dev_name		= "ttyS",
@@ -2910,6 +2908,7 @@ static struct uart_driver serial8250_reg = {
 	.minor			= 64,
 	.cons			= SERIAL8250_CONSOLE,
 };
+EXPORT_SYMBOL_GPL(serial8250_reg);
 
 /*
  * early_serial_setup - early registration for 8250 ports
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index c174c90..687af38 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -96,6 +96,8 @@ struct uart_8250_port {
 	void			(*dl_write)(struct uart_8250_port *, int);
 };
 
+extern struct uart_driver serial8250_reg;
+
 int serial8250_register_8250_port(struct uart_8250_port *);
 void serial8250_unregister_port(int line);
 void serial8250_suspend_port(int line);
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [RFC PATCH v3 4/4] UART: Add dummy devices to test the enumeration.
  2012-12-06  9:21 ` [RFC PATCH v3 0/4] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
                     ` (2 preceding siblings ...)
  2012-12-06  9:22   ` [RFC PATCH v3 3/4] UART / 8250: Add declearation of serial8250 driver Lv Zheng
@ 2012-12-06  9:22   ` Lv Zheng
  3 siblings, 0 replies; 71+ messages in thread
From: Lv Zheng @ 2012-12-06  9:22 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox,
	Mika Westerberg
  Cc: linux-acpi, linux-serial, Lv Zheng

This is a test patch that should not be merged to any of the published
Linux source tree.

1. The result of the UART dummy target device is as follows:

# udevadm monitor --kernel --environment > ~/uart.uevents
# echo add > /sys/bus/uart/uevent
# echo add > /sys/bus/uart/devices/DUMMY/uevent
# cat ~/uart.uevents
monitor will print the received events for:
KERNEL - the kernel uevent

KERNEL[252.443458] add      /bus/uart (bus)
ACTION=add
DEVPATH=/bus/uart
SEQNUM=1142
SUBSYSTEM=bus

KERNEL[268.491709] add      /devices/platform/serial8250/DUMMY (uart)
ACTION=add
DEVPATH=/devices/platform/serial8250/DUMMY
DEVTYPE=uart_device
MODALIAS=uart:DUMMY
SEQNUM=1144
SUBSYSTEM=uart

# cat /sys/bus/uart/devices/DUMMY/modalias
uart:DUMMY
# cat /sys/bus/uart/devices/DUMMY/tty_dev
ttyS3
# cat /sys/bus/uart/devices/DUMMY/tty_attrs
115200 8N1 HW SW
# cat /sys/bus/uart/devices/DUMMY/modem_lines
LE:RTS,

# ls -l /sys/bus/uart/devices/
DUMMY -> ../../../devices/platform/serial8250/DUMMY
# ls -l /sys/devices/platform/serial8250/DUMMY/
subsystem -> ../../../../bus/uart
host_node -> ../tty/ttyS3
# ls -l /sys/devices/platform/serial8250/tty/ttyS3/
target_node -> ../../DUMMY

2. The result of the UART customized DSDT target device is as follows:

The test is based on the following customized DSDT (containing the dummy
uart host adapter INTF000 and target device INTF001):
  Device (UA00)
  {
      Name (_HID, "INTF000")  // _HID: Hardware ID
      Name (RBUF, ResourceTemplate ()
      {
          Memory32Fixed (ReadWrite,
              0x00000000,         // Address Base
              0x00001000)         // Address Length
      })
      Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
      {
          Return (RBUF)
      }
      Method (_STA, 0, NotSerialized)  // _STA: Status
      {
          Return (0x0F)
      }
      Device (BTH0)
      {
          Name (_HID, "INTF001")  // _HID: Hardware ID
          Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
          {
              Name (UBUF, ResourceTemplate ()
              {
                  UartSerialBus (0x0001C200, DataBitsEight, StopBitsOne,
                      0xC0, LittleEndian, ParityTypeNone, FlowControlHardware,
                      0x0020, 0x0020, "\\_SB.PCI0.UA00",
                      0x00, ResourceConsumer, ,
                      )
              })
              Return (UBUF)
          }
          Method (_STA, 0, NotSerialized)  // _STA: Status
          {
              Return (0x0F)
          }
      }
  }

# udevadm monitor --kernel --environment > ~/uart.uevents
# echo add > /sys/bus/uart/uevent
# echo add > /sys/bus/uart/devices/INTF001/uevent
# cat ~/uart.uevents
monitor will print the received events for:
KERNEL - the kernel uevent

KERNEL[252.443458] add      /bus/uart (bus)
ACTION=add
DEVPATH=/bus/uart
SEQNUM=1142
SUBSYSTEM=bus

KERNEL[268.491709] add      /devices/platform/INTF000:00/INTF001:00 (uart)
ACTION=add
DEVPATH=/devices/platform/INTF000:00/INTF001:00
DEVTYPE=uart_device
MODALIAS=uart:INTF001:00
SEQNUM=1144
SUBSYSTEM=uart

# cat /sys/bus/uart/devices/INTF001:00/modalias
uart:INTF001:00
# cat /sys/bus/uart/devices/INTF001:00/tty_dev
ttyS0
# cat /sys/bus/uart/devices/INTF001:00/tty_attrs
115200 8N0 HW
# cat /sys/bus/uart/devices/INTF001:00/modem_lines
LE:RTS,CTS,

# ls -l /sys/bus/uart/devices/
INTF001:00 -> ../../../devices/platform/INTF000:00/INTF001:00
# ls -l /sys/devices/platform/INTF000:00/INTF001:00/
firmware_node -> ../../../LNXSYSTM:00/device:00/INTF000:00/INTF001:00
subsystem -> ../../../../bus/uart
host_node -> ../tty/ttyS0
# ls -l /sys/devices/platform/INTF000:00/tty/ttyS0/
target_node -> ../../INTF001:00
# ls -l /sys/bus/acpi/INTF001:00/
physical_node -> ../../../../pnp0/00:00
physical_node1 -> ../../../../platform/INTF000:00/INTF001:00

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/acpi/scan.c                  |    1 +
 drivers/tty/serial/8250/8250.c       |   12 ++++
 drivers/tty/serial/8250/8250_dummy.c |  129 ++++++++++++++++++++++++++++++++++
 drivers/tty/serial/8250/Kconfig      |   10 +++
 drivers/tty/serial/8250/Makefile     |    1 +
 drivers/tty/serial/serial_bus.c      |   17 +++++
 include/linux/serial_bus.h           |    5 ++
 7 files changed, 175 insertions(+)
 create mode 100644 drivers/tty/serial/8250/8250_dummy.c

diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 4dd13e4..95c7528 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -36,6 +36,7 @@ static const char *dummy_hid = "device";
 static const struct acpi_device_id acpi_platform_device_ids[] = {
 
 	{ "PNP0D40" },
+	{ "INTF000" },
 
 	{ }
 };
diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c
index af78374..745e05d 100644
--- a/drivers/tty/serial/8250/8250.c
+++ b/drivers/tty/serial/8250/8250.c
@@ -35,6 +35,7 @@
 #include <linux/serial_core.h>
 #include <linux/serial.h>
 #include <linux/serial_8250.h>
+#include <linux/serial_bus.h>
 #include <linux/nmi.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
@@ -2986,6 +2987,8 @@ void serial8250_resume_port(int line)
 	uart_resume_port(&serial8250_reg, port);
 }
 
+struct uart_device *uart_dummy;
+
 /*
  * Register a set of serial devices attached to a platform device.  The
  * list is terminated with a zero flags entry, which means we expect
@@ -2996,6 +2999,7 @@ static int __devinit serial8250_probe(struct platform_device *dev)
 	struct plat_serial8250_port *p = dev->dev.platform_data;
 	struct uart_8250_port uart;
 	int ret, i, irqflag = 0;
+	unsigned int dummy_line;
 
 	memset(&uart, 0, sizeof(uart));
 
@@ -3031,6 +3035,13 @@ static int __devinit serial8250_probe(struct platform_device *dev)
 				p->irq, ret);
 		}
 	}
+
+	dummy_line = serial8250_ports[nr_uarts-1].port.line;
+	pr_info("registering DUMMY at line %d.\n", dummy_line);
+	uart_dummy = uart_register_dummy(&dev->dev,
+					 serial8250_reg.tty_driver,
+					 dummy_line);
+
 	return 0;
 }
 
@@ -3041,6 +3052,7 @@ static int __devexit serial8250_remove(struct platform_device *dev)
 {
 	int i;
 
+	uart_unregister_device(uart_dummy);
 	for (i = 0; i < nr_uarts; i++) {
 		struct uart_8250_port *up = &serial8250_ports[i];
 
diff --git a/drivers/tty/serial/8250/8250_dummy.c b/drivers/tty/serial/8250/8250_dummy.c
new file mode 100644
index 0000000..c5ec064
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_dummy.c
@@ -0,0 +1,129 @@
+/*
+ * 8250_dummy.c: Dummy 8250 UART target device enumerator
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_bus.h>
+#include <linux/acpi.h>
+#include <linux/acpi_uart.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct dummy8250_data {
+	int	last_lcr;
+	int	line;
+};
+
+static void dummy8250_serial_out(struct uart_port *p, int offset, int value)
+{
+}
+
+static unsigned int dummy8250_serial_in(struct uart_port *p, int offset)
+{
+	return 0;
+}
+
+struct klist *dummy8250_mgr;
+int dummy8250_num;
+
+static int __devinit dummy8250_probe(struct platform_device *pdev)
+{
+	struct uart_8250_port uart = {};
+	struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	struct dummy8250_data *data;
+#ifdef CONFIG_ACPI_UART
+	struct klist *mgr;
+#endif
+
+	dev_info(&pdev->dev, "1\n");
+	if (!regs) {
+		dev_err(&pdev->dev, "no registers defined\n");
+		return -EINVAL;
+	}
+
+	dev_info(&pdev->dev, "2\n");
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+	uart.port.private_data = data;
+
+	spin_lock_init(&uart.port.lock);
+	uart.port.mapbase = regs->start;
+	uart.port.type = PORT_8250;
+	uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP |
+		UPF_FIXED_PORT | UPF_FIXED_TYPE;
+	uart.port.dev = &pdev->dev;
+
+	uart.port.iotype = UPIO_MEM;
+	uart.port.serial_in = dummy8250_serial_in;
+	uart.port.serial_out = dummy8250_serial_out;
+	uart.port.uartclk = 40000000;
+
+	dev_info(&pdev->dev, "3\n");
+	data->line = serial8250_register_8250_port(&uart);
+	if (data->line < 0)
+		return data->line;
+
+#ifdef CONFIG_ACPI_UART
+	dev_info(&pdev->dev, "4\n");
+	mgr = acpi_uart_register_devices(dummy8250_mgr,
+					 &pdev->dev,
+					 serial8250_reg.tty_driver,
+					 data->line);
+	if (mgr) {
+		dummy8250_mgr = mgr;
+		dummy8250_num++;
+	}
+#endif
+	dev_info(&pdev->dev, "5\n");
+	platform_set_drvdata(pdev, data);
+
+	return 0;
+}
+
+static int __devexit dummy8250_remove(struct platform_device *pdev)
+{
+	struct dummy8250_data *data = platform_get_drvdata(pdev);
+
+#ifdef CONFIG_ACPI_UART
+	dummy8250_num--;
+	if (!dummy8250_num)
+		acpi_uart_unregister_devices(dummy8250_mgr);
+#endif
+	serial8250_unregister_port(data->line);
+
+	return 0;
+}
+
+static const struct acpi_device_id dummy8250_match[] = {
+	{ .id = "INTF000" },
+	{ /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(acpi, dummy8250_match);
+
+static struct platform_driver dummy8250_platform_driver = {
+	.driver = {
+		.name			= "dummy-uart",
+		.owner			= THIS_MODULE,
+		.acpi_match_table	= dummy8250_match,
+	},
+	.probe				= dummy8250_probe,
+	.remove				= __devexit_p(dummy8250_remove),
+};
+
+module_platform_driver(dummy8250_platform_driver);
+
+MODULE_AUTHOR("Lv Zheng");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Dummy 8250 serial port driver");
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index f3d283f..3ba480a 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -270,6 +270,16 @@ config SERIAL_8250_DW
 	  Selecting this option will enable handling of the extra features
 	  present in the Synopsys DesignWare APB UART.
 
+config SERIAL_8250_DUMMY
+	tristate "Support for dummy ACPI 8250"
+	depends on SERIAL_8250 && ACPI
+	help
+	  Selecting this option will enable a test UART target device DUMMY
+	  under the ISA serial8250 and a test UART host adapter INTF000
+	  as an platform device for the purpose of testing the ACPI UART
+	  enumeration support.
+	  If unsure, say "N" here.
+
 config SERIAL_8250_EM
 	tristate "Support for Emma Mobile intergrated serial port"
 	depends on SERIAL_8250 && ARM && HAVE_CLK
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index 108fe7f..fb82aa9 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -19,3 +19,4 @@ obj-$(CONFIG_SERIAL_8250_HUB6)		+= 8250_hub6.o
 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_DUMMY)		+= 8250_dummy.o
diff --git a/drivers/tty/serial/serial_bus.c b/drivers/tty/serial/serial_bus.c
index 66c112d..033dd37 100644
--- a/drivers/tty/serial/serial_bus.c
+++ b/drivers/tty/serial/serial_bus.c
@@ -393,6 +393,23 @@ void uart_del_adapter(struct klist *adap)
 }
 EXPORT_SYMBOL_GPL(uart_del_adapter);
 
+static struct uart_board_info dummy_target = {
+	.type = "DUMMY",
+	.baud = 115200,
+	.cflag = CS8 | CSTOPB | CRTSCTS,
+	.iflag = (IXON | IXOFF),
+	.mctrl = TIOCM_LE | TIOCM_RTS,
+};
+
+struct uart_device *uart_register_dummy(struct device *dev,
+					struct tty_driver *drv,
+					unsigned int line)
+{
+	dummy_target.line = line;
+	return uart_register_device(NULL, dev, drv, &dummy_target);
+}
+EXPORT_SYMBOL_GPL(uart_register_dummy);
+
 struct bus_type uart_bus_type = {
 	.name		= "uart",
 };
diff --git a/include/linux/serial_bus.h b/include/linux/serial_bus.h
index 1955d3e..7eb74b9 100644
--- a/include/linux/serial_bus.h
+++ b/include/linux/serial_bus.h
@@ -118,4 +118,9 @@ struct device *uart_tty_find(struct tty_driver *drv, unsigned int line,
 			     struct device *dev);
 void uart_tty_name(struct tty_driver *driver, unsigned int line, char *p);
 
+/* Test dummy device registration */
+struct uart_device *uart_register_dummy(struct device *dev,
+					struct tty_driver *drv,
+					unsigned int line);
+
 #endif /* LINUX_SERIAL_BUS_H */
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* Re: [RFC PATCH v3 1/4] UART: Add UART subsystem as a bus.
  2012-12-06  9:21   ` [RFC PATCH v3 1/4] UART: Add UART subsystem as a bus Lv Zheng
@ 2012-12-06 13:40     ` Alan Cox
  2012-12-07  3:52       ` Zheng, Lv
  0 siblings, 1 reply; 71+ messages in thread
From: Alan Cox @ 2012-12-06 13:40 UTC (permalink / raw)
  To: Lv Zheng
  Cc: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Mika Westerberg,
	linux-acpi, linux-serial

> enumerated and userspace can obtain the hotplug event of the UART
> target devices. Linux kernel is lack of an overall enumeration
> mechanism for UART target devices.

ls /sys/class/tty/

> In order to send uevent, a device need to be a class device or a bus
> device. This patch introduces a bus_type subsystem to manage the new

I still don't see why the existing tty class interfaces cannot be used,
the hints from ACPI (or anywhere else) cannot be attached as properties
to the tty driver and device sysfs

Alan

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [RFC PATCH 1/3] UART: Add UART subsystem as a bus.
  2012-12-06  1:26           ` Zheng, Lv
  2012-12-06  1:55             ` Zheng, Lv
@ 2012-12-06 13:53             ` Alan Cox
  2012-12-07  4:54               ` Zheng, Lv
  1 sibling, 1 reply; 71+ messages in thread
From: Alan Cox @ 2012-12-06 13:53 UTC (permalink / raw)
  To: Zheng, Lv
  Cc: Mika Westerberg, Alan Cox, Brown, Len, Wysocki, Rafael J,
	Greg Kroah-Hartman, linux-acpi, linux-serial, Heikki Krogerus,
	Mathias Nyman, Huang, Ying

> > We just need a set of type names for the sysfs node I think "bluetooth", "ups",
> > "loconet", "serial", "modem", "cir" etc...
> 
> Is it a good idea to introduce uart_device driver in the kernel to fill a new 'ldisc' member in the uart_device or to load ldisc by default for the corresponding tty_port?

No but it can provide information to help user space. In many cases the
decision isn't about a line discipline but about automatically setting
permissions or linking ports to the right driver.

The hints need to be generic - they can come from open firmware, from pci
identifiers, from ACPI and so on.

> Shall we change the uart_bus to the tty_bus, then introduce tty_host / tty_target for the bus?

We have a tty class - is that not sufficient ?

Alan

^ permalink raw reply	[flat|nested] 71+ messages in thread

* RE: [RFC PATCH v3 1/4] UART: Add UART subsystem as a bus.
  2012-12-06 13:40     ` Alan Cox
@ 2012-12-07  3:52       ` Zheng, Lv
  2012-12-07 10:22         ` Alan Cox
  0 siblings, 1 reply; 71+ messages in thread
From: Zheng, Lv @ 2012-12-07  3:52 UTC (permalink / raw)
  To: Alan Cox
  Cc: Brown, Len, Wysocki, Rafael J, Greg Kroah-Hartman,
	Mika Westerberg, linux-acpi, linux-serial,
	H. Peter Anvin (hpa@linux.intel.com)

> > enumerated and userspace can obtain the hotplug event of the UART
> > target devices. Linux kernel is lack of an overall enumeration
> > mechanism for UART target devices.
> 
> ls /sys/class/tty/

I'm searching for a target "physical" device while the "tty device" looks more like a "functional" device.

IMO, an ideal driver model is:
Creating a strict IO topology for all of the physical buses w/ physical devices created within this hierarchy, then make the abstract things "class devices", including the "functional devices" and the "filter devices".
The examples of the "physical devices": platform device, uart_device, i2c_client, spi_device
The examples of the "functional devices": tty device, i2c_adapter, spi_master
The examples of the "filter devices": ppp device

If this rule is strictly obeyed by all of the subsystems, then:
1. the hardware PM can be added to the physical devices
2. the functional devices will inc/dec the PM reference count for the physical devices <- this can help to add request based runtime PM
3. the other filter devices/drivers can call the APIs provided by the functional devices, some of them may affect the PM ref count.
Implementing OS guided power management might be simpler:
1. if a bus offered PM support, it should appear in the bus_type
2. if a platform offered PM support, it should appear as PM_domain
Currently, Linux allows enabling of PM support for device_type, if things are cleaned up, the use of device_type PM can be reduced.

If a device was handling hardware stuff including the PM, it should be a "physical device".
If a device was offering communication means, it should be a "functional device".
If a device was using the communication means, it should be a "filter device".
So how could a tty device be a physical device? If we enable PM for it, dpm_list order will be crushed, issues might occur on system suspending/resuming.

> > In order to send uevent, a device need to be a class device or a bus
> > device. This patch introduces a bus_type subsystem to manage the new
> 
> I still don't see why the existing tty class interfaces cannot be used, the hints
> from ACPI (or anywhere else) cannot be attached as properties to the tty driver
> and device sysfs

I checked the PM call chain, then found the followings:

On system suspending:
  adapter.suspend -> for_each_port(uart_suspend_port) -> uart_change_pm(3) -> uart_ops->pm -> uart_port->pm
On system resuming:
  adapter.resume -> for_each_port(uart_resume_port) -> uart_change_pm(0) -> uart_ops->pm -> uart_port->pm
Where the adapter could be platform devices.

On tty file opening:
 uart_open -> uart_change_pm(0) -> uart_ops->pm -> uart_port->pm
On tty file closing:
 uart_close -> uart_change_pm(3) -> uart_ops->pm -> uart_port->pm

And I checked the PM implementation, most of the drivers will enable/disable the "HOST" side port clock there and only few of them will lower the required terminal lines to tell the target devices to enter a low power state.
So I was just thinking if the tty device was used as physical device in the community, it might be used as "host" side physical device, and my patch was trying to enable a "target" side physical device.

Adding driver support for such "target physical uart device" sounds a good idea. But I need an explicit approval for adding the kernel resident driver.

Best regards
-Lv


^ permalink raw reply	[flat|nested] 71+ messages in thread

* RE: [RFC PATCH 1/3] UART: Add UART subsystem as a bus.
  2012-12-06 13:53             ` Alan Cox
@ 2012-12-07  4:54               ` Zheng, Lv
  2012-12-07  5:41                 ` Zheng, Lv
  2012-12-07 10:25                 ` Alan Cox
  0 siblings, 2 replies; 71+ messages in thread
From: Zheng, Lv @ 2012-12-07  4:54 UTC (permalink / raw)
  To: Alan Cox
  Cc: Mika Westerberg, Alan Cox, Brown, Len, Wysocki, Rafael J,
	Greg Kroah-Hartman, linux-acpi, linux-serial, Huang, Ying

> > > We just need a set of type names for the sysfs node I think
> > > "bluetooth", "ups", "loconet", "serial", "modem", "cir" etc...
> >
> > Is it a good idea to introduce uart_device driver in the kernel to fill a new
> 'ldisc' member in the uart_device or to load ldisc by default for the
> corresponding tty_port?
> 
> No but it can provide information to help user space. In many cases the
> decision isn't about a line discipline but about automatically setting permissions
> or linking ports to the right driver.
> 
> The hints need to be generic - they can come from open firmware, from pci
> identifiers, from ACPI and so on.

Userspace need to know more than a simple type name to match the driver.
For example,
Atheros BT will send wrapped HCI packets from their HSU BT adapters, so they need to see "ath3000" rather than "bluetooth" to load a kernel hci protocol module for the device.
As we can see, in the i2c/spi world, there is only "type" field filling w/ chip name, not 2 fields - "type" filled w/ generic type and "id" filled w/ chip name.
Userspace can figure the generic "type" from the un-generic chip name like "type".
I just wonder will there be issues caused by following this design?

> > Shall we change the uart_bus to the tty_bus, then introduce tty_host /
> tty_target for the bus?
> 
> We have a tty class - is that not sufficient ?

As I mentioned in the previous mail, I'll keep the current naming rule until I see some objections strong enough.

-Lv

^ permalink raw reply	[flat|nested] 71+ messages in thread

* RE: [RFC PATCH 1/3] UART: Add UART subsystem as a bus.
  2012-12-07  4:54               ` Zheng, Lv
@ 2012-12-07  5:41                 ` Zheng, Lv
  2012-12-07  7:24                   ` Huang Ying
  2012-12-07 10:27                   ` Alan Cox
  2012-12-07 10:25                 ` Alan Cox
  1 sibling, 2 replies; 71+ messages in thread
From: Zheng, Lv @ 2012-12-07  5:41 UTC (permalink / raw)
  To: Zheng, Lv, Alan Cox
  Cc: Mika Westerberg, Alan Cox, Brown, Len, Wysocki, Rafael J,
	Greg Kroah-Hartman, linux-acpi, linux-serial, Huang, Ying

> > > > We just need a set of type names for the sysfs node I think
> > > > "bluetooth", "ups", "loconet", "serial", "modem", "cir" etc...
> > >
> > > Is it a good idea to introduce uart_device driver in the kernel to
> > > fill a new
> > 'ldisc' member in the uart_device or to load ldisc by default for the
> > corresponding tty_port?
> >
> > No but it can provide information to help user space. In many cases
> > the decision isn't about a line discipline but about automatically
> > setting permissions or linking ports to the right driver.
> >
> > The hints need to be generic - they can come from open firmware, from
> > pci identifiers, from ACPI and so on.
> 
> Userspace need to know more than a simple type name to match the driver.
> For example,
> Atheros BT will send wrapped HCI packets from their HSU BT adapters, so they
> need to see "ath3000" rather than "bluetooth" to load a kernel hci protocol
> module for the device.
> As we can see, in the i2c/spi world, there is only "type" field filling w/ chip name,
> not 2 fields - "type" filled w/ generic type and "id" filled w/ chip name.
> Userspace can figure the generic "type" from the un-generic chip name like
> "type".
> I just wonder will there be issues caused by following this design?

Ying has suggested me to include all of the HID/CIDs as a list into board_info.
If OF need this feature, I'll split type into a type and an "ID" list.
The module_alias file can export all of the IDs and the device name can be the "type:index".
I can make the modifications on this if you think this is better.

> > > Shall we change the uart_bus to the tty_bus, then introduce tty_host
> > > /
> > tty_target for the bus?
> >
> > We have a tty class - is that not sufficient ?
> 
> As I mentioned in the previous mail, I'll keep the current naming rule until I see
> some objections strong enough.

This statement is confusing in English, let me clarify this.
If the reasons for the uart_bus in the previous email are not correct, I'll change everything named as uart_xxx to tty_xxx, then the tty_bus is probably not needed, and I can use tty class after the decision.

Best regards
-Lv

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [RFC PATCH 1/3] UART: Add UART subsystem as a bus.
  2012-12-07  5:41                 ` Zheng, Lv
@ 2012-12-07  7:24                   ` Huang Ying
  2012-12-07 10:27                   ` Alan Cox
  1 sibling, 0 replies; 71+ messages in thread
From: Huang Ying @ 2012-12-07  7:24 UTC (permalink / raw)
  To: Zheng, Lv, Alan Cox
  Cc: Mika Westerberg, Alan Cox, Brown, Len, Wysocki, Rafael J,
	Greg Kroah-Hartman, linux-acpi, linux-serial

Hi, Alan and Lv,

On Thu, 2012-12-06 at 22:41 -0700, Zheng, Lv wrote:
[snip]
> > > > Shall we change the uart_bus to the tty_bus, then introduce tty_host
> > > > /
> > > tty_target for the bus?
> > >
> > > We have a tty class - is that not sufficient ?
> > 
> > As I mentioned in the previous mail, I'll keep the current naming rule until I see
> > some objections strong enough.
> 
> This statement is confusing in English, let me clarify this.
> If the reasons for the uart_bus in the previous email are not correct, I'll change everything named as uart_xxx to tty_xxx, then the tty_bus is probably not needed, and I can use tty class after the decision.

If my understanding were correct, the point is not about name it as
uart_bus or tty_bus, but why do we need a bus instead of just use tty
class for that.

If we use tty class to export tty target device information, it can be
something as follow:

$ cd /sys/devices/platform/serial8250/tty/ttyS0
$ ls
slave_type
slave_id
slave_compat_ids
slave_attr
modem_lines

If we use uart or tty bus to export tty target device information, it
can be something as follow:

$ cd /sys/devices/platform/serial8250/bluetooth:00
$ ls
type
id
compt_ids
attr
modem_lines
...
$ cd /sys/bus/(uart|tty)/devices
$ ls
bluetooth:00

Both works.  But IMHO, the second one appears more natural and
consistent, just like other hardware devices in system.  And create
struct device for each target devices make it easier to do power
management etc, for example, user can enable/disable runtime power
management via bluetooth:00/power/control, just like other hardware
devices.

Best Regards,
Huang Ying



^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [RFC PATCH v3 1/4] UART: Add UART subsystem as a bus.
  2012-12-07  3:52       ` Zheng, Lv
@ 2012-12-07 10:22         ` Alan Cox
  0 siblings, 0 replies; 71+ messages in thread
From: Alan Cox @ 2012-12-07 10:22 UTC (permalink / raw)
  To: Zheng, Lv
  Cc: Alan Cox, Brown, Len, Wysocki, Rafael J, Greg Kroah-Hartman,
	Mika Westerberg, linux-acpi, linux-serial,
	H. Peter Anvin (hpa@linux.intel.com)

> > ls /sys/class/tty/
> 
> I'm searching for a target "physical" device while the "tty device" looks more like a "functional" device.

We don't currently properly link that up - but we should. It does however
provide the place to attach information like modem lines and intended use.

> If this rule is strictly obeyed by all of the subsystems, then:
> 1. the hardware PM can be added to the physical devices

To get the heirarchy you need to the tty to be in the PCI bus or other
bus where it actually sits ? Consider an ACPI enumerated port whose
address space is behind a PCI bridge.

> 1. if a bus offered PM support, it should appear in the bus_type

Which would usually be the PCI bus (and an ACPI enumerated device behind
a PCI bus needs to go via PCI to keep all the PCI counts correct)

> If a device was handling hardware stuff including the PM, it should be a "physical device".
> If a device was offering communication means, it should be a "functional device".
> If a device was using the communication means, it should be a "filter device".

A tty is all three. We have the physical device (wires and pins), the
functional object (an open tty) and filters (line disciplines I guess).

> And I checked the PM implementation, most of the drivers will enable/disable the "HOST" side port clock there and only few of them will lower the required terminal lines to tell the target devices to enter a low power state.

Because generally that breaks stuff. For example if you lower the carrier
to some UPS devices they conclude the PC has been turned off. This leads
to highly undesirable behaviour. As a result it's generally been user
space that handles such details.

> So I was just thinking if the tty device was used as physical device in the community, it might be used as "host" side physical device, and my patch was trying to enable a "target" side physical device.
> 
> Adding driver support for such "target physical uart device" sounds a good idea. But I need an explicit approval for
adding the kernel resident driver.

Not entirely sure what you mean by kernel resident driver here.

As of 3.7 we have a 'tty_port' for every tty. Not every port corresponds
to an actual physical device (eg pty doesn't, nor do mux channels). We
could certainly keep a bus driver reference in the tty_port if it helps.
If NULL means "none/not set" we can also gradually make drivers set it
correctly.

At that point you would be able to find the underlying device from any
tty port, and you can find any tty_port from the tty as of 3.7.


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [RFC PATCH 1/3] UART: Add UART subsystem as a bus.
  2012-12-07  4:54               ` Zheng, Lv
  2012-12-07  5:41                 ` Zheng, Lv
@ 2012-12-07 10:25                 ` Alan Cox
  1 sibling, 0 replies; 71+ messages in thread
From: Alan Cox @ 2012-12-07 10:25 UTC (permalink / raw)
  To: Zheng, Lv
  Cc: Mika Westerberg, Alan Cox, Brown, Len, Wysocki, Rafael J,
	Greg Kroah-Hartman, linux-acpi, linux-serial, Huang, Ying

On Fri, 7 Dec 2012 04:54:40 +0000
"Zheng, Lv" <lv.zheng@intel.com> wrote:

> > > > We just need a set of type names for the sysfs node I think
> > > > "bluetooth", "ups", "loconet", "serial", "modem", "cir" etc...
> > >
> > > Is it a good idea to introduce uart_device driver in the kernel to fill a new
> > 'ldisc' member in the uart_device or to load ldisc by default for the
> > corresponding tty_port?
> > 
> > No but it can provide information to help user space. In many cases the
> > decision isn't about a line discipline but about automatically setting permissions
> > or linking ports to the right driver.
> > 
> > The hints need to be generic - they can come from open firmware, from pci
> > identifiers, from ACPI and so on.
> 
> Userspace need to know more than a simple type name to match the driver.

It's just an enumeration - and unlike numbers names don't usually collide
or need hard management

> For example,
> Atheros BT will send wrapped HCI packets from their HSU BT adapters, so they need to see "ath3000" rather than "bluetooth" to load a kernel hci protocol module for the device.

Fine so we could use "bluetooth/atheros" or similar. Easily dealt with

Alan

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [RFC PATCH 1/3] UART: Add UART subsystem as a bus.
  2012-12-07  5:41                 ` Zheng, Lv
  2012-12-07  7:24                   ` Huang Ying
@ 2012-12-07 10:27                   ` Alan Cox
  1 sibling, 0 replies; 71+ messages in thread
From: Alan Cox @ 2012-12-07 10:27 UTC (permalink / raw)
  To: Zheng, Lv
  Cc: Mika Westerberg, Alan Cox, Brown, Len, Wysocki, Rafael J,
	Greg Kroah-Hartman, linux-acpi, linux-serial, Huang, Ying

> Ying has suggested me to include all of the HID/CIDs as a list into board_info.

if HID/CID values are also provided by ACPI then that makes sense

> If OF need this feature, I'll split type into a type and an "ID" list.

That would be good. Code that wants generic answers can check the type,
code that wants identifiers or needs detail can check the ids

Alan

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [RFC PATCH v4 1/3] TTY: Add TTY slave enumeration support.
  2013-01-09  9:17 ` [RFC PATCH v4 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
@ 2013-01-09  9:17   ` Lv Zheng
  2013-01-09  9:17   ` Lv Zheng
                     ` (4 subsequent siblings)
  5 siblings, 0 replies; 71+ messages in thread
From: Lv Zheng @ 2013-01-09  9:17 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox,
	Mika Westerberg
  Cc: linux-acpi, linux-serial, Lv Zheng

In the recent ACPI 5.0 specification updates, firmwares are provided the
possibilities to enumerate the UART slave devices known to the platform
vendors.
There are the needs in Linux to utilize the benefits:
1. hotplug uevent
2. serial configuration
Currently, only serial cards on the specific bus (ex. PCMCIA) can be
enumerated and userspace can obtain the hotplug event of the UART target
devices. Linux kernel is lack of an overall enumeration mechanism for
UART slave devices.
In order to send uevent, a device need to be a class device or a bus
device. This patch introduces a tty_enum bus since the enumerated slave
devices are expected to be physical devices.
When the UART slave devices are created, userspace uevent rules can
pass the creation details to the userspace driver managers
(ex. hciattach), then the device managers can read hardware IDs and the
serial configurations from the exported device attributes to match and
configure a userspace TTY device driver.
The created slave devices will be automatically unregistered when the
associated TTY ports are destructed.

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/tty/Kconfig              |    3 +
 drivers/tty/Makefile             |    1 +
 drivers/tty/serial/Kconfig       |    1 +
 drivers/tty/serial/serial_core.c |    2 +-
 drivers/tty/tty_enum.c           |  356 ++++++++++++++++++++++++++++++++++++++
 drivers/tty/tty_port.c           |   42 ++++-
 include/linux/mod_devicetable.h  |    6 +
 include/linux/tty.h              |  104 +++++++++++
 8 files changed, 511 insertions(+), 4 deletions(-)
 create mode 100644 drivers/tty/tty_enum.c

diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index 0ecf22b..e0ef9db 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig
@@ -151,6 +151,9 @@ config LEGACY_PTY_COUNT
 	  When not in use, each legacy PTY occupies 12 bytes on 32-bit
 	  architectures and 24 bytes on 64-bit architectures.
 
+config TTY_ENUM
+	bool
+
 config BFIN_JTAG_COMM
 	tristate "Blackfin JTAG Communication"
 	depends on BLACKFIN
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile
index 2953059..f4eb30c 100644
--- a/drivers/tty/Makefile
+++ b/drivers/tty/Makefile
@@ -1,5 +1,6 @@
 obj-y				+= tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o \
 				   tty_buffer.o tty_port.o tty_mutex.o
+obj-$(CONFIG_TTY_ENUM)		+= tty_enum.o
 obj-$(CONFIG_LEGACY_PTYS)	+= pty.o
 obj-$(CONFIG_UNIX98_PTYS)	+= pty.o
 obj-$(CONFIG_AUDIT)		+= tty_audit.o
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 59c23d0..c3dee46 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -751,6 +751,7 @@ config SERIAL_HS_LPC32XX_CONSOLE
 
 config SERIAL_CORE
 	tristate
+	select TTY_ENUM
 
 config SERIAL_CORE_CONSOLE
 	bool
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 2c7230a..d6cb417 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -2662,7 +2662,7 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
 	/*
 	 * Remove the devices from the tty layer
 	 */
-	tty_unregister_device(drv->tty_driver, uport->line);
+	tty_port_unregister_device(port, drv->tty_driver, uport->line);
 
 	if (port->tty)
 		tty_vhangup(port->tty);
diff --git a/drivers/tty/tty_enum.c b/drivers/tty/tty_enum.c
new file mode 100644
index 0000000..2e91cb6
--- /dev/null
+++ b/drivers/tty/tty_enum.c
@@ -0,0 +1,356 @@
+
+/*
+ * tty_enum.c - TTY enumeration support
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+
+
+static LIST_HEAD(tty_bus_id_list);
+DEFINE_MUTEX(tty_bus_lock);
+
+struct tty_bus_id {
+	char bus_id[TTY_NAME_SIZE];
+	unsigned int instance_no;
+	struct list_head node;
+};
+
+static ssize_t tty_slave_show_name(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct tty_slave *tts = to_tty_slave(dev);
+	return sprintf(buf, "%s\n", tts->name);
+}
+
+static int create_modalias(struct tty_slave *tts, char *modalias, int size)
+{
+	int len;
+	int count;
+	int i;
+
+	if (!tts->nr_ids)
+		return 0;
+
+	len = snprintf(modalias, size, "%s:", TTY_MODULE_PREFIX);
+	size -= len;
+
+	for (i = 0; i < tts->nr_ids; i++) {
+		count = snprintf(&modalias[len], size, "%s:", tts->ids[i]);
+		if (count < 0 || count >= size)
+			return -EINVAL;
+		len += count;
+		size -= count;
+	}
+
+	modalias[len] = '\0';
+	return len;
+}
+
+static ssize_t tty_slave_show_modalias(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct tty_slave *tts = to_tty_slave(dev);
+	size_t len;
+
+	/* Device has no IDs or string is >1024 */
+	len = create_modalias(tts, buf, 1024);
+	if (len <= 0)
+		return 0;
+	buf[len++] = '\n';
+	return len;
+}
+
+static ssize_t tty_slave_show_tty_attrs(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct tty_slave *tts = to_tty_slave(dev);
+	int len = 0;
+
+	/* baud rate */
+	len += sprintf(buf+len, "%d ", tts->baud);
+
+	/* data bits */
+	switch (tts->cflag & CSIZE) {
+	case CS5:
+		len += sprintf(buf+len, "5");
+		break;
+	case CS6:
+		len += sprintf(buf+len, "6");
+		break;
+	case CS7:
+		len += sprintf(buf+len, "7");
+		break;
+	case CS8:
+	default:
+		len += sprintf(buf+len, "8");
+		break;
+	}
+
+	/* parity */
+	if (tts->cflag & PARODD)
+		len += sprintf(buf+len, "O");
+	else if (tts->cflag & PARENB)
+		len += sprintf(buf+len, "E");
+	else
+		len += sprintf(buf+len, "N");
+
+	/* stop bits */
+	len += sprintf(buf+len, "%d", tts->cflag & CSTOPB ? 1 : 0);
+
+	/* HW/SW control */
+	if (tts->cflag & CRTSCTS)
+		len += sprintf(buf+len, " HW");
+	if ((tts->iflag & (IXON|IXOFF|IXANY)) != 0)
+		len += sprintf(buf+len, " SW");
+
+	len += sprintf(buf+len, "\n");
+	return len;
+}
+
+static ssize_t tty_slave_show_modem_lines(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct tty_slave *tts = to_tty_slave(dev);
+	int len = 0;
+
+	/* endian */
+	if (tts->mctrl & TIOCM_LE)
+		len += sprintf(buf+len, "LE:");
+	else
+		len += sprintf(buf+len, "BE:");
+
+	/* terminal lines */
+	if (tts->mctrl & TIOCM_DTR)
+		len += sprintf(buf+len, "DTR,");
+	if (tts->mctrl & TIOCM_RTS)
+		len += sprintf(buf+len, "RTS,");
+
+	/* modem lines */
+	if (tts->mctrl & TIOCM_CTS)
+		len += sprintf(buf+len, "CTS,");
+	if (tts->mctrl & TIOCM_CAR)
+		len += sprintf(buf+len, "CAR,");
+	if (tts->mctrl & TIOCM_RNG)
+		len += sprintf(buf+len, "RNG,");
+	if (tts->mctrl & TIOCM_DSR)
+		len += sprintf(buf+len, "DSR,");
+
+	len += sprintf(buf+len, "\n");
+	return len;
+}
+
+static DEVICE_ATTR(name, S_IRUGO, tty_slave_show_name, NULL);
+static DEVICE_ATTR(modalias, S_IRUGO, tty_slave_show_modalias, NULL);
+static DEVICE_ATTR(tty_attrs, S_IRUGO, tty_slave_show_tty_attrs, NULL);
+static DEVICE_ATTR(modem_lines, S_IRUGO, tty_slave_show_modem_lines, NULL);
+
+static struct attribute *tty_slave_attrs[] = {
+	&dev_attr_name.attr,
+	/* coldplug: modprobe $(cat .../modalias) */
+	&dev_attr_modalias.attr,
+	&dev_attr_tty_attrs.attr,
+	&dev_attr_modem_lines.attr,
+	NULL,
+};
+
+static struct attribute_group tty_slave_group = {
+	.attrs	= tty_slave_attrs,
+};
+
+static const struct attribute_group *tty_slave_groups[] = {
+	&tty_slave_group,
+	NULL,
+};
+
+#ifdef CONFIG_HOTPLUG
+static int tty_slave_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct tty_slave *tts = to_tty_slave(dev);
+	int len;
+
+	if (!tts->nr_ids)
+		return 0;
+
+	if (add_uevent_var(env, "MODALIAS="))
+		return -ENOMEM;
+	len = create_modalias(tts, &env->buf[env->buflen - 1],
+			      sizeof(env->buf) - env->buflen);
+	if (len >= (sizeof(env->buf) - env->buflen))
+		return -ENOMEM;
+	env->buflen += len;
+
+	dev_dbg(dev, "uevent\n");
+	return 0;
+}
+#else
+#define tty_slave_uevent	NULL
+#endif
+
+static void tty_slave_release(struct device *dev)
+{
+	struct tty_slave *tts = to_tty_slave(dev);
+
+	kfree(tts);
+	/* Test code to see if slave device get released */
+	BUG();
+}
+
+struct device_type tty_slave_type = {
+	.name		= "tty_slave",
+	.groups		= tty_slave_groups,
+	.uevent		= tty_slave_uevent,
+	.release	= tty_slave_release,
+};
+EXPORT_SYMBOL_GPL(tty_slave_type);
+
+/**
+ * tty_bus_register_slave - register a physical tty slave device
+ * @port: the tty port
+ * @info: the tty slave device description
+ *
+ * Initialize and add a physical tty slave device.
+ *
+ * This returns the new physical tty slave device, which may be saved for
+ * later use with device_unregister; or NULL to indicate an error.
+ */
+struct tty_slave *tty_bus_register_slave(struct tty_port *port,
+		struct tty_board_info const *info)
+{
+	struct tty_slave *tts;
+	struct tty_bus_id *bus_id, *new_bus_id;
+	struct device *dev = NULL;
+	int i;
+	int status;
+	int found = 0;
+
+	/* Drivers having not called tty_port_register_device, should not
+	 * call this API.
+	 */
+	BUG_ON(!info || !port || !port->dev);
+	dev = port->dev;
+
+	tts = kzalloc(sizeof(struct tty_slave) + info->nr_ids * TTY_NAME_SIZE,
+		      GFP_KERNEL);
+	if (!tts) {
+		dev_err(dev, "Failed to alloc tty_slave %s.\n", info->type);
+		return NULL;
+	}
+
+	new_bus_id = kzalloc(sizeof(struct tty_bus_id), GFP_KERNEL);
+	if (!new_bus_id) {
+		dev_err(dev, "Failed to alloc tty_bus_id for %s.\n",
+			info->type);
+		goto fail;
+	}
+
+	tts->dev.parent = dev;
+	tts->dev.bus = &tty_enum_bus;
+	tts->dev.type = &tty_slave_type;
+
+	tts->dev.platform_data = info->platform_data;
+	if (info->archdata)
+		tts->dev.archdata = *info->archdata;
+
+	tts->baud = info->baud;
+	tts->cflag = info->cflag;
+	tts->iflag = info->iflag;
+	tts->mctrl = info->mctrl;
+	tts->irq = info->irq;
+
+	strlcpy(tts->name, info->type, TTY_NAME_SIZE);
+	for (i = 0; i < info->nr_ids; i++)
+		strlcpy(tts->ids[i], info->ids[i], TTY_NAME_SIZE);
+	tts->nr_ids = info->nr_ids;
+
+	mutex_lock(&tty_bus_lock);
+	list_for_each_entry(bus_id, &tty_bus_id_list, node) {
+		if (!strcmp(bus_id->bus_id, info->type)) {
+			bus_id->instance_no++;
+			found = 1;
+			kfree(new_bus_id);
+			break;
+		}
+	}
+	if (!found) {
+		bus_id = new_bus_id;
+		strcpy(bus_id->bus_id, info->type);
+		bus_id->instance_no = 0;
+		list_add_tail(&bus_id->node, &tty_bus_id_list);
+	}
+	dev_set_name(&tts->dev, "%s:%02x", bus_id->bus_id, bus_id->instance_no);
+	mutex_unlock(&tty_bus_lock);
+
+	status = device_register(&tts->dev);
+	if (status) {
+		dev_err(dev, "Failed to register slave device %s.\n",
+			info->type);
+		goto fail;
+	}
+	dev_info(dev, "Registered slave device %s.\n", info->type);
+
+	return tts;
+
+fail:
+	kfree(tts);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(tty_bus_register_slave);
+
+static int __unregister(struct device *dev, void *null)
+{
+	if (is_tty_slave(dev))
+		device_unregister(dev);
+	return 0;
+}
+
+void tty_bus_unregister_slaves(struct tty_port *port)
+{
+	if (port->dev)
+		(void)device_for_each_child(port->dev, NULL, __unregister);
+}
+EXPORT_SYMBOL_GPL(tty_bus_unregister_slaves);
+
+void tty_bus_register_master(struct tty_port *port, struct device *dev)
+{
+	BUG_ON(!port);
+	if (dev)
+		port->dev = get_device(dev);
+}
+EXPORT_SYMBOL_GPL(tty_bus_register_master);
+
+void tty_bus_unregister_master(struct tty_port *port)
+{
+	BUG_ON(!port);
+	if (port->dev) {
+		tty_bus_unregister_slaves(port);
+		put_device(port->dev);
+		port->dev = NULL;
+	}
+}
+EXPORT_SYMBOL_GPL(tty_bus_unregister_master);
+
+struct bus_type tty_enum_bus = {
+	.name		= "tty_enum",
+};
+EXPORT_SYMBOL_GPL(tty_enum_bus);
+
+int __init tty_bus_init(void)
+{
+	return bus_register(&tty_enum_bus);
+}
+
+postcore_initcall(tty_bus_init);
diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c
index b7ff59d..8864124 100644
--- a/drivers/tty/tty_port.c
+++ b/drivers/tty/tty_port.c
@@ -69,8 +69,13 @@ struct device *tty_port_register_device(struct tty_port *port,
 		struct tty_driver *driver, unsigned index,
 		struct device *device)
 {
+	struct device *dev;
+
 	tty_port_link_device(port, driver, index);
-	return tty_register_device(driver, index, device);
+	dev = tty_register_device(driver, index, device);
+	tty_bus_register_master(port, dev);
+
+	return dev;
 }
 EXPORT_SYMBOL_GPL(tty_port_register_device);
 
@@ -92,12 +97,33 @@ struct device *tty_port_register_device_attr(struct tty_port *port,
 		struct device *device, void *drvdata,
 		const struct attribute_group **attr_grp)
 {
+	struct device *dev;
+
 	tty_port_link_device(port, driver, index);
-	return tty_register_device_attr(driver, index, device, drvdata,
-			attr_grp);
+	dev = tty_register_device_attr(driver, index, device, drvdata,
+				       attr_grp);
+	tty_bus_register_master(port, dev);
+
+	return dev;
 }
 EXPORT_SYMBOL_GPL(tty_port_register_device_attr);
 
+/**
+ * tty_port_unregister_device_attr - unregister tty device
+ * @port: tty_port of the device
+ * @driver: tty_driver for this device
+ * @index: index of the tty
+ *
+ * The reverse call for tty_register_device.
+ */
+void tty_port_unregister_device(struct tty_port *port,
+		struct tty_driver *driver, unsigned index)
+{
+	tty_bus_unregister_master(port);
+	tty_unregister_device(driver, index);
+}
+EXPORT_SYMBOL_GPL(tty_port_unregister_device);
+
 int tty_port_alloc_xmit_buf(struct tty_port *port)
 {
 	/* We may sleep in get_zeroed_page() */
@@ -132,6 +158,16 @@ EXPORT_SYMBOL(tty_port_free_xmit_buf);
  */
 void tty_port_destroy(struct tty_port *port)
 {
+	/*
+	 * XXX: TTY Driver Cleanup
+	 *
+	 * If you got panic here, it means you have enabled the TTY bus
+	 * enumeration support for your TTY driver but you haven't updated
+	 * your TTY driver code to call the tty_port_unregister_device
+	 * instead of the tty_unregister_device as a destruction
+	 * corresponding to the tty_port_register_device.
+	 */
+	BUG_ON(port->dev);
 	tty_buffer_free_all(port);
 }
 EXPORT_SYMBOL(tty_port_destroy);
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index fed3def..f1c4875 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -433,6 +433,12 @@ struct rpmsg_device_id {
 	char name[RPMSG_NAME_SIZE];
 };
 
+/* tty */
+
+/* TTY slave name size */
+#define TTY_NAME_SIZE	32
+#define TTY_MODULE_PREFIX "tty:"
+
 /* i2c */
 
 #define I2C_NAME_SIZE	20
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 8db1b56..36eb9d1 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -9,6 +9,7 @@
 #include <linux/tty_ldisc.h>
 #include <linux/mutex.h>
 #include <linux/tty_flags.h>
+#include <linux/mod_devicetable.h>
 #include <uapi/linux/tty.h>
 
 
@@ -154,6 +155,105 @@ struct tty_bufhead {
 
 struct device;
 struct signal_struct;
+struct tty_slave;
+struct tty_board_info;
+
+/**
+ * struct tty_board_info - template for slave device creation
+ * @type: chip type, to initialize tty_slave.name
+ * @cflag: termio cflag, preferred termio cflag to be used to communicate
+ *         with this slave device
+ * @iflag: termio iflag, preferred termio iflag to be used to communicate
+ *         with this slave device
+ * @mctrl: termio mctrl, preferred termio mctrl to be used to communicate
+ *         with this slave device
+ * @baud: termio baud, preferred termio baud rate to be used to communicate
+ *        with this slave device
+ * @irq: stored in tty_slave.irq
+ * @platform_data: stored in tty_slave.dev.platform_data
+ * @archdata: copied into tty_slave.dev.archdata
+ * @nr_ids: number of IDs
+ * @ids: ID strings
+ *
+ * tty_board_info is used to build tables of information listing TTY
+ * devices that are present. This information is used to grow the driver
+ * model tree. For add-on boards, tty_bus_register_slave() does this
+ * dynamically with the host side physical device already known.
+ */
+struct tty_board_info {
+	char type[TTY_NAME_SIZE];
+	unsigned int cflag;	/* termio cflag */
+	unsigned int iflag;	/* termio iflag */
+	unsigned int mctrl;	/* modem ctrl settings */
+	unsigned int baud;
+	int irq;
+	void *platform_data;
+	struct dev_archdata *archdata;
+
+	int nr_ids;
+	/* This must be the last member of tty_board_info */
+	char ids[0][TTY_NAME_SIZE];
+};
+
+/**
+ * struct tty_slave - represent a TTY slave device
+ * @name: Indicates the type of the device, usually a chip name that's
+ *        generic enough to hide second-sourcing and compatible revisions
+ * @cflag: preferred termio cflag used to communicate with this slave
+ *         device
+ * @iflag: preferred termio iflag used to communicate with this slave
+ *         device
+ * @mctrl: preferred termio mctrl used to communicate with this slave
+ *         device
+ * @baud: preferred termio baud rate used to communicate with this slave
+ *        device
+ * @irq: indicates the IRQ generated by this slave device (if any)
+ * @dev: driver model device node for the slave device
+ * @nr_ids: number of IDs
+ * @ids: ID strings
+ *
+ * A tty_slave identifies a single device (i.e. chip) connected to a tty
+ * port.
+ */
+struct tty_slave {
+	char			name[TTY_NAME_SIZE];
+	unsigned int		cflag;	/* termio cflag */
+	unsigned int		iflag;	/* termio iflag */
+	unsigned int		mctrl;	/* modem ctrl settings */
+	unsigned int		baud;
+	int			irq;	/* irq issued by device */
+	struct device		dev;
+	int			nr_ids;
+	char			ids[0][TTY_NAME_SIZE];
+};
+
+extern struct device_type tty_slave_type;
+
+#define is_tty_slave(d) ((d) && (d)->type == &tty_slave_type)
+#define to_tty_slave(d) container_of(d, struct tty_slave, dev)
+
+static inline struct tty_slave *tty_verify_slave(struct device *dev)
+{
+	return is_tty_slave(dev) ? to_tty_slave(dev) : NULL;
+}
+
+#ifdef CONFIG_TTY_ENUM
+struct tty_slave *tty_bus_register_slave(struct tty_port *port,
+		struct tty_board_info const *info);
+void tty_bus_unregister_slaves(struct tty_port *port);
+void tty_bus_register_master(struct tty_port *port, struct device *dev);
+void tty_bus_unregister_master(struct tty_port *port);
+#else
+static inline struct tty_slave *tty_bus_register_slave(struct tty_port *port,
+		struct tty_board_info const *info)
+{
+	return NULL;
+}
+static inline void tty_bus_unregister_slaves(struct tty_port *port) {}
+static inline void tty_bus_register_master(struct tty_port *port,
+		struct device *dev) {}
+static inline void tty_bus_unregister_master(struct tty_port *port) {}
+#endif
 
 /*
  * Port level information. Each device keeps its own port level information
@@ -189,6 +289,7 @@ struct tty_port_operations {
 	
 struct tty_port {
 	struct tty_bufhead	buf;		/* Locked internally */
+	struct device		*dev;		/* Registered tty device */
 	struct tty_struct	*tty;		/* Back pointer */
 	struct tty_struct	*itty;		/* internal back ptr */
 	const struct tty_port_operations *ops;	/* Port operations */
@@ -325,6 +426,7 @@ extern void console_init(void);
 extern int vcs_init(void);
 
 extern struct class *tty_class;
+extern struct bus_type tty_enum_bus;
 
 /**
  *	tty_kref_get		-	get a tty reference
@@ -453,6 +555,8 @@ extern struct device *tty_port_register_device_attr(struct tty_port *port,
 		struct tty_driver *driver, unsigned index,
 		struct device *device, void *drvdata,
 		const struct attribute_group **attr_grp);
+extern void tty_port_unregister_device(struct tty_port *port,
+		struct tty_driver *driver, unsigned index);
 extern int tty_port_alloc_xmit_buf(struct tty_port *port);
 extern void tty_port_free_xmit_buf(struct tty_port *port);
 extern void tty_port_destroy(struct tty_port *port);
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [RFC PATCH v4 1/3] TTY: Add TTY slave enumeration support.
  2013-01-09  9:17 ` [RFC PATCH v4 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
  2013-01-09  9:17   ` [RFC PATCH v4 1/3] TTY: Add TTY slave enumeration support Lv Zheng
@ 2013-01-09  9:17   ` Lv Zheng
  2013-01-09  9:18   ` [RFC PATCH v4 2/3] ACPI / UART: Add ACPI enumeration support for UART Lv Zheng
                     ` (3 subsequent siblings)
  5 siblings, 0 replies; 71+ messages in thread
From: Lv Zheng @ 2013-01-09  9:17 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox,
	Mika Westerberg
  Cc: linux-acpi, linux-serial, Lv Zheng

In the recent ACPI 5.0 specification updates, firmwares are provided the
possibilities to enumerate the UART slave devices known to the platform
vendors.
There are the needs in Linux to utilize the benefits:
1. hotplug uevent
2. serial configuration
Currently, only serial cards on the specific bus (ex. PCMCIA) can be
enumerated and userspace can obtain the hotplug event of the UART target
devices. Linux kernel is lack of an overall enumeration mechanism for
UART slave devices.
In order to send uevent, a device need to be a class device or a bus
device. This patch introduces a tty_enum bus since the enumerated slave
devices are expected to be physical devices.
When the UART slave devices are created, userspace uevent rules can
pass the creation details to the userspace driver managers
(ex. hciattach), then the device managers can read hardware IDs and the
serial configurations from the exported device attributes to match and
configure a userspace TTY device driver.
The created slave devices will be automatically unregistered when the
associated TTY ports are destructed.

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/tty/Kconfig              |    3 +
 drivers/tty/Makefile             |    1 +
 drivers/tty/serial/Kconfig       |    1 +
 drivers/tty/serial/serial_core.c |    2 +-
 drivers/tty/tty_enum.c           |  356 ++++++++++++++++++++++++++++++++++++++
 drivers/tty/tty_port.c           |   42 ++++-
 include/linux/mod_devicetable.h  |    6 +
 include/linux/tty.h              |  104 +++++++++++
 8 files changed, 511 insertions(+), 4 deletions(-)
 create mode 100644 drivers/tty/tty_enum.c

diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index 0ecf22b..e0ef9db 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig
@@ -151,6 +151,9 @@ config LEGACY_PTY_COUNT
 	  When not in use, each legacy PTY occupies 12 bytes on 32-bit
 	  architectures and 24 bytes on 64-bit architectures.
 
+config TTY_ENUM
+	bool
+
 config BFIN_JTAG_COMM
 	tristate "Blackfin JTAG Communication"
 	depends on BLACKFIN
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile
index 2953059..f4eb30c 100644
--- a/drivers/tty/Makefile
+++ b/drivers/tty/Makefile
@@ -1,5 +1,6 @@
 obj-y				+= tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o \
 				   tty_buffer.o tty_port.o tty_mutex.o
+obj-$(CONFIG_TTY_ENUM)		+= tty_enum.o
 obj-$(CONFIG_LEGACY_PTYS)	+= pty.o
 obj-$(CONFIG_UNIX98_PTYS)	+= pty.o
 obj-$(CONFIG_AUDIT)		+= tty_audit.o
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 59c23d0..c3dee46 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -751,6 +751,7 @@ config SERIAL_HS_LPC32XX_CONSOLE
 
 config SERIAL_CORE
 	tristate
+	select TTY_ENUM
 
 config SERIAL_CORE_CONSOLE
 	bool
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 2c7230a..d6cb417 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -2662,7 +2662,7 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
 	/*
 	 * Remove the devices from the tty layer
 	 */
-	tty_unregister_device(drv->tty_driver, uport->line);
+	tty_port_unregister_device(port, drv->tty_driver, uport->line);
 
 	if (port->tty)
 		tty_vhangup(port->tty);
diff --git a/drivers/tty/tty_enum.c b/drivers/tty/tty_enum.c
new file mode 100644
index 0000000..2e91cb6
--- /dev/null
+++ b/drivers/tty/tty_enum.c
@@ -0,0 +1,356 @@
+
+/*
+ * tty_enum.c - TTY enumeration support
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+
+
+static LIST_HEAD(tty_bus_id_list);
+DEFINE_MUTEX(tty_bus_lock);
+
+struct tty_bus_id {
+	char bus_id[TTY_NAME_SIZE];
+	unsigned int instance_no;
+	struct list_head node;
+};
+
+static ssize_t tty_slave_show_name(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct tty_slave *tts = to_tty_slave(dev);
+	return sprintf(buf, "%s\n", tts->name);
+}
+
+static int create_modalias(struct tty_slave *tts, char *modalias, int size)
+{
+	int len;
+	int count;
+	int i;
+
+	if (!tts->nr_ids)
+		return 0;
+
+	len = snprintf(modalias, size, "%s:", TTY_MODULE_PREFIX);
+	size -= len;
+
+	for (i = 0; i < tts->nr_ids; i++) {
+		count = snprintf(&modalias[len], size, "%s:", tts->ids[i]);
+		if (count < 0 || count >= size)
+			return -EINVAL;
+		len += count;
+		size -= count;
+	}
+
+	modalias[len] = '\0';
+	return len;
+}
+
+static ssize_t tty_slave_show_modalias(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct tty_slave *tts = to_tty_slave(dev);
+	size_t len;
+
+	/* Device has no IDs or string is >1024 */
+	len = create_modalias(tts, buf, 1024);
+	if (len <= 0)
+		return 0;
+	buf[len++] = '\n';
+	return len;
+}
+
+static ssize_t tty_slave_show_tty_attrs(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct tty_slave *tts = to_tty_slave(dev);
+	int len = 0;
+
+	/* baud rate */
+	len += sprintf(buf+len, "%d ", tts->baud);
+
+	/* data bits */
+	switch (tts->cflag & CSIZE) {
+	case CS5:
+		len += sprintf(buf+len, "5");
+		break;
+	case CS6:
+		len += sprintf(buf+len, "6");
+		break;
+	case CS7:
+		len += sprintf(buf+len, "7");
+		break;
+	case CS8:
+	default:
+		len += sprintf(buf+len, "8");
+		break;
+	}
+
+	/* parity */
+	if (tts->cflag & PARODD)
+		len += sprintf(buf+len, "O");
+	else if (tts->cflag & PARENB)
+		len += sprintf(buf+len, "E");
+	else
+		len += sprintf(buf+len, "N");
+
+	/* stop bits */
+	len += sprintf(buf+len, "%d", tts->cflag & CSTOPB ? 1 : 0);
+
+	/* HW/SW control */
+	if (tts->cflag & CRTSCTS)
+		len += sprintf(buf+len, " HW");
+	if ((tts->iflag & (IXON|IXOFF|IXANY)) != 0)
+		len += sprintf(buf+len, " SW");
+
+	len += sprintf(buf+len, "\n");
+	return len;
+}
+
+static ssize_t tty_slave_show_modem_lines(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct tty_slave *tts = to_tty_slave(dev);
+	int len = 0;
+
+	/* endian */
+	if (tts->mctrl & TIOCM_LE)
+		len += sprintf(buf+len, "LE:");
+	else
+		len += sprintf(buf+len, "BE:");
+
+	/* terminal lines */
+	if (tts->mctrl & TIOCM_DTR)
+		len += sprintf(buf+len, "DTR,");
+	if (tts->mctrl & TIOCM_RTS)
+		len += sprintf(buf+len, "RTS,");
+
+	/* modem lines */
+	if (tts->mctrl & TIOCM_CTS)
+		len += sprintf(buf+len, "CTS,");
+	if (tts->mctrl & TIOCM_CAR)
+		len += sprintf(buf+len, "CAR,");
+	if (tts->mctrl & TIOCM_RNG)
+		len += sprintf(buf+len, "RNG,");
+	if (tts->mctrl & TIOCM_DSR)
+		len += sprintf(buf+len, "DSR,");
+
+	len += sprintf(buf+len, "\n");
+	return len;
+}
+
+static DEVICE_ATTR(name, S_IRUGO, tty_slave_show_name, NULL);
+static DEVICE_ATTR(modalias, S_IRUGO, tty_slave_show_modalias, NULL);
+static DEVICE_ATTR(tty_attrs, S_IRUGO, tty_slave_show_tty_attrs, NULL);
+static DEVICE_ATTR(modem_lines, S_IRUGO, tty_slave_show_modem_lines, NULL);
+
+static struct attribute *tty_slave_attrs[] = {
+	&dev_attr_name.attr,
+	/* coldplug: modprobe $(cat .../modalias) */
+	&dev_attr_modalias.attr,
+	&dev_attr_tty_attrs.attr,
+	&dev_attr_modem_lines.attr,
+	NULL,
+};
+
+static struct attribute_group tty_slave_group = {
+	.attrs	= tty_slave_attrs,
+};
+
+static const struct attribute_group *tty_slave_groups[] = {
+	&tty_slave_group,
+	NULL,
+};
+
+#ifdef CONFIG_HOTPLUG
+static int tty_slave_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct tty_slave *tts = to_tty_slave(dev);
+	int len;
+
+	if (!tts->nr_ids)
+		return 0;
+
+	if (add_uevent_var(env, "MODALIAS="))
+		return -ENOMEM;
+	len = create_modalias(tts, &env->buf[env->buflen - 1],
+			      sizeof(env->buf) - env->buflen);
+	if (len >= (sizeof(env->buf) - env->buflen))
+		return -ENOMEM;
+	env->buflen += len;
+
+	dev_dbg(dev, "uevent\n");
+	return 0;
+}
+#else
+#define tty_slave_uevent	NULL
+#endif
+
+static void tty_slave_release(struct device *dev)
+{
+	struct tty_slave *tts = to_tty_slave(dev);
+
+	kfree(tts);
+	/* Test code to see if slave device get released */
+	BUG();
+}
+
+struct device_type tty_slave_type = {
+	.name		= "tty_slave",
+	.groups		= tty_slave_groups,
+	.uevent		= tty_slave_uevent,
+	.release	= tty_slave_release,
+};
+EXPORT_SYMBOL_GPL(tty_slave_type);
+
+/**
+ * tty_bus_register_slave - register a physical tty slave device
+ * @port: the tty port
+ * @info: the tty slave device description
+ *
+ * Initialize and add a physical tty slave device.
+ *
+ * This returns the new physical tty slave device, which may be saved for
+ * later use with device_unregister; or NULL to indicate an error.
+ */
+struct tty_slave *tty_bus_register_slave(struct tty_port *port,
+		struct tty_board_info const *info)
+{
+	struct tty_slave *tts;
+	struct tty_bus_id *bus_id, *new_bus_id;
+	struct device *dev = NULL;
+	int i;
+	int status;
+	int found = 0;
+
+	/* Drivers having not called tty_port_register_device, should not
+	 * call this API.
+	 */
+	BUG_ON(!info || !port || !port->dev);
+	dev = port->dev;
+
+	tts = kzalloc(sizeof(struct tty_slave) + info->nr_ids * TTY_NAME_SIZE,
+		      GFP_KERNEL);
+	if (!tts) {
+		dev_err(dev, "Failed to alloc tty_slave %s.\n", info->type);
+		return NULL;
+	}
+
+	new_bus_id = kzalloc(sizeof(struct tty_bus_id), GFP_KERNEL);
+	if (!new_bus_id) {
+		dev_err(dev, "Failed to alloc tty_bus_id for %s.\n",
+			info->type);
+		goto fail;
+	}
+
+	tts->dev.parent = dev;
+	tts->dev.bus = &tty_enum_bus;
+	tts->dev.type = &tty_slave_type;
+
+	tts->dev.platform_data = info->platform_data;
+	if (info->archdata)
+		tts->dev.archdata = *info->archdata;
+
+	tts->baud = info->baud;
+	tts->cflag = info->cflag;
+	tts->iflag = info->iflag;
+	tts->mctrl = info->mctrl;
+	tts->irq = info->irq;
+
+	strlcpy(tts->name, info->type, TTY_NAME_SIZE);
+	for (i = 0; i < info->nr_ids; i++)
+		strlcpy(tts->ids[i], info->ids[i], TTY_NAME_SIZE);
+	tts->nr_ids = info->nr_ids;
+
+	mutex_lock(&tty_bus_lock);
+	list_for_each_entry(bus_id, &tty_bus_id_list, node) {
+		if (!strcmp(bus_id->bus_id, info->type)) {
+			bus_id->instance_no++;
+			found = 1;
+			kfree(new_bus_id);
+			break;
+		}
+	}
+	if (!found) {
+		bus_id = new_bus_id;
+		strcpy(bus_id->bus_id, info->type);
+		bus_id->instance_no = 0;
+		list_add_tail(&bus_id->node, &tty_bus_id_list);
+	}
+	dev_set_name(&tts->dev, "%s:%02x", bus_id->bus_id, bus_id->instance_no);
+	mutex_unlock(&tty_bus_lock);
+
+	status = device_register(&tts->dev);
+	if (status) {
+		dev_err(dev, "Failed to register slave device %s.\n",
+			info->type);
+		goto fail;
+	}
+	dev_info(dev, "Registered slave device %s.\n", info->type);
+
+	return tts;
+
+fail:
+	kfree(tts);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(tty_bus_register_slave);
+
+static int __unregister(struct device *dev, void *null)
+{
+	if (is_tty_slave(dev))
+		device_unregister(dev);
+	return 0;
+}
+
+void tty_bus_unregister_slaves(struct tty_port *port)
+{
+	if (port->dev)
+		(void)device_for_each_child(port->dev, NULL, __unregister);
+}
+EXPORT_SYMBOL_GPL(tty_bus_unregister_slaves);
+
+void tty_bus_register_master(struct tty_port *port, struct device *dev)
+{
+	BUG_ON(!port);
+	if (dev)
+		port->dev = get_device(dev);
+}
+EXPORT_SYMBOL_GPL(tty_bus_register_master);
+
+void tty_bus_unregister_master(struct tty_port *port)
+{
+	BUG_ON(!port);
+	if (port->dev) {
+		tty_bus_unregister_slaves(port);
+		put_device(port->dev);
+		port->dev = NULL;
+	}
+}
+EXPORT_SYMBOL_GPL(tty_bus_unregister_master);
+
+struct bus_type tty_enum_bus = {
+	.name		= "tty_enum",
+};
+EXPORT_SYMBOL_GPL(tty_enum_bus);
+
+int __init tty_bus_init(void)
+{
+	return bus_register(&tty_enum_bus);
+}
+
+postcore_initcall(tty_bus_init);
diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c
index b7ff59d..8864124 100644
--- a/drivers/tty/tty_port.c
+++ b/drivers/tty/tty_port.c
@@ -69,8 +69,13 @@ struct device *tty_port_register_device(struct tty_port *port,
 		struct tty_driver *driver, unsigned index,
 		struct device *device)
 {
+	struct device *dev;
+
 	tty_port_link_device(port, driver, index);
-	return tty_register_device(driver, index, device);
+	dev = tty_register_device(driver, index, device);
+	tty_bus_register_master(port, dev);
+
+	return dev;
 }
 EXPORT_SYMBOL_GPL(tty_port_register_device);
 
@@ -92,12 +97,33 @@ struct device *tty_port_register_device_attr(struct tty_port *port,
 		struct device *device, void *drvdata,
 		const struct attribute_group **attr_grp)
 {
+	struct device *dev;
+
 	tty_port_link_device(port, driver, index);
-	return tty_register_device_attr(driver, index, device, drvdata,
-			attr_grp);
+	dev = tty_register_device_attr(driver, index, device, drvdata,
+				       attr_grp);
+	tty_bus_register_master(port, dev);
+
+	return dev;
 }
 EXPORT_SYMBOL_GPL(tty_port_register_device_attr);
 
+/**
+ * tty_port_unregister_device_attr - unregister tty device
+ * @port: tty_port of the device
+ * @driver: tty_driver for this device
+ * @index: index of the tty
+ *
+ * The reverse call for tty_register_device.
+ */
+void tty_port_unregister_device(struct tty_port *port,
+		struct tty_driver *driver, unsigned index)
+{
+	tty_bus_unregister_master(port);
+	tty_unregister_device(driver, index);
+}
+EXPORT_SYMBOL_GPL(tty_port_unregister_device);
+
 int tty_port_alloc_xmit_buf(struct tty_port *port)
 {
 	/* We may sleep in get_zeroed_page() */
@@ -132,6 +158,16 @@ EXPORT_SYMBOL(tty_port_free_xmit_buf);
  */
 void tty_port_destroy(struct tty_port *port)
 {
+	/*
+	 * XXX: TTY Driver Cleanup
+	 *
+	 * If you got panic here, it means you have enabled the TTY bus
+	 * enumeration support for your TTY driver but you haven't updated
+	 * your TTY driver code to call the tty_port_unregister_device
+	 * instead of the tty_unregister_device as a destruction
+	 * corresponding to the tty_port_register_device.
+	 */
+	BUG_ON(port->dev);
 	tty_buffer_free_all(port);
 }
 EXPORT_SYMBOL(tty_port_destroy);
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index fed3def..f1c4875 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -433,6 +433,12 @@ struct rpmsg_device_id {
 	char name[RPMSG_NAME_SIZE];
 };
 
+/* tty */
+
+/* TTY slave name size */
+#define TTY_NAME_SIZE	32
+#define TTY_MODULE_PREFIX "tty:"
+
 /* i2c */
 
 #define I2C_NAME_SIZE	20
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 8db1b56..36eb9d1 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -9,6 +9,7 @@
 #include <linux/tty_ldisc.h>
 #include <linux/mutex.h>
 #include <linux/tty_flags.h>
+#include <linux/mod_devicetable.h>
 #include <uapi/linux/tty.h>
 
 
@@ -154,6 +155,105 @@ struct tty_bufhead {
 
 struct device;
 struct signal_struct;
+struct tty_slave;
+struct tty_board_info;
+
+/**
+ * struct tty_board_info - template for slave device creation
+ * @type: chip type, to initialize tty_slave.name
+ * @cflag: termio cflag, preferred termio cflag to be used to communicate
+ *         with this slave device
+ * @iflag: termio iflag, preferred termio iflag to be used to communicate
+ *         with this slave device
+ * @mctrl: termio mctrl, preferred termio mctrl to be used to communicate
+ *         with this slave device
+ * @baud: termio baud, preferred termio baud rate to be used to communicate
+ *        with this slave device
+ * @irq: stored in tty_slave.irq
+ * @platform_data: stored in tty_slave.dev.platform_data
+ * @archdata: copied into tty_slave.dev.archdata
+ * @nr_ids: number of IDs
+ * @ids: ID strings
+ *
+ * tty_board_info is used to build tables of information listing TTY
+ * devices that are present. This information is used to grow the driver
+ * model tree. For add-on boards, tty_bus_register_slave() does this
+ * dynamically with the host side physical device already known.
+ */
+struct tty_board_info {
+	char type[TTY_NAME_SIZE];
+	unsigned int cflag;	/* termio cflag */
+	unsigned int iflag;	/* termio iflag */
+	unsigned int mctrl;	/* modem ctrl settings */
+	unsigned int baud;
+	int irq;
+	void *platform_data;
+	struct dev_archdata *archdata;
+
+	int nr_ids;
+	/* This must be the last member of tty_board_info */
+	char ids[0][TTY_NAME_SIZE];
+};
+
+/**
+ * struct tty_slave - represent a TTY slave device
+ * @name: Indicates the type of the device, usually a chip name that's
+ *        generic enough to hide second-sourcing and compatible revisions
+ * @cflag: preferred termio cflag used to communicate with this slave
+ *         device
+ * @iflag: preferred termio iflag used to communicate with this slave
+ *         device
+ * @mctrl: preferred termio mctrl used to communicate with this slave
+ *         device
+ * @baud: preferred termio baud rate used to communicate with this slave
+ *        device
+ * @irq: indicates the IRQ generated by this slave device (if any)
+ * @dev: driver model device node for the slave device
+ * @nr_ids: number of IDs
+ * @ids: ID strings
+ *
+ * A tty_slave identifies a single device (i.e. chip) connected to a tty
+ * port.
+ */
+struct tty_slave {
+	char			name[TTY_NAME_SIZE];
+	unsigned int		cflag;	/* termio cflag */
+	unsigned int		iflag;	/* termio iflag */
+	unsigned int		mctrl;	/* modem ctrl settings */
+	unsigned int		baud;
+	int			irq;	/* irq issued by device */
+	struct device		dev;
+	int			nr_ids;
+	char			ids[0][TTY_NAME_SIZE];
+};
+
+extern struct device_type tty_slave_type;
+
+#define is_tty_slave(d) ((d) && (d)->type == &tty_slave_type)
+#define to_tty_slave(d) container_of(d, struct tty_slave, dev)
+
+static inline struct tty_slave *tty_verify_slave(struct device *dev)
+{
+	return is_tty_slave(dev) ? to_tty_slave(dev) : NULL;
+}
+
+#ifdef CONFIG_TTY_ENUM
+struct tty_slave *tty_bus_register_slave(struct tty_port *port,
+		struct tty_board_info const *info);
+void tty_bus_unregister_slaves(struct tty_port *port);
+void tty_bus_register_master(struct tty_port *port, struct device *dev);
+void tty_bus_unregister_master(struct tty_port *port);
+#else
+static inline struct tty_slave *tty_bus_register_slave(struct tty_port *port,
+		struct tty_board_info const *info)
+{
+	return NULL;
+}
+static inline void tty_bus_unregister_slaves(struct tty_port *port) {}
+static inline void tty_bus_register_master(struct tty_port *port,
+		struct device *dev) {}
+static inline void tty_bus_unregister_master(struct tty_port *port) {}
+#endif
 
 /*
  * Port level information. Each device keeps its own port level information
@@ -189,6 +289,7 @@ struct tty_port_operations {
 	
 struct tty_port {
 	struct tty_bufhead	buf;		/* Locked internally */
+	struct device		*dev;		/* Registered tty device */
 	struct tty_struct	*tty;		/* Back pointer */
 	struct tty_struct	*itty;		/* internal back ptr */
 	const struct tty_port_operations *ops;	/* Port operations */
@@ -325,6 +426,7 @@ extern void console_init(void);
 extern int vcs_init(void);
 
 extern struct class *tty_class;
+extern struct bus_type tty_enum_bus;
 
 /**
  *	tty_kref_get		-	get a tty reference
@@ -453,6 +555,8 @@ extern struct device *tty_port_register_device_attr(struct tty_port *port,
 		struct tty_driver *driver, unsigned index,
 		struct device *device, void *drvdata,
 		const struct attribute_group **attr_grp);
+extern void tty_port_unregister_device(struct tty_port *port,
+		struct tty_driver *driver, unsigned index);
 extern int tty_port_alloc_xmit_buf(struct tty_port *port);
 extern void tty_port_free_xmit_buf(struct tty_port *port);
 extern void tty_port_destroy(struct tty_port *port);
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [RFC PATCH v4 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART.
  2012-12-03  3:39 [RFC PATCH 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
                   ` (4 preceding siblings ...)
  2012-12-06  9:21 ` [RFC PATCH v3 0/4] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
@ 2013-01-09  9:17 ` Lv Zheng
  2013-01-09  9:17   ` [RFC PATCH v4 1/3] TTY: Add TTY slave enumeration support Lv Zheng
                     ` (5 more replies)
  2013-01-09  9:17 ` [RFC PATCH v4 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
                   ` (4 subsequent siblings)
  10 siblings, 6 replies; 71+ messages in thread
From: Lv Zheng @ 2013-01-09  9:17 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox,
	Mika Westerberg
  Cc: linux-acpi, linux-serial, Lv Zheng

ACPI 5.0 specification introduces enumeration support for SPB buses. This
patch set adds the UART serial bus enumeration support to Linux using such
mechanism.
NOTE: PATCH 3 is a test patch, should not be merged into any published
      Linux source tree.
This patch set is based on the tty tree/tty-next branch.

History:
v1:
  1. Add "uart" bus subsystem.
  2. Add physical UART target device and description
     (uart_device/uart_board_info).
  3. Add logical UART host adapter (klist).
  4. Add UART target device attributes (tty_dev/tty_attrs/modem_lines).
  5. Create tty_device<->uart_device links (host_node/target_node).
v2.
  1. Change UART layer related stuff to non-UART related.
  2. Modify the order of the function parameters.
v3:
  1. Add comments for the uart_board_info and the uart_device.
  2. Add platform_data/archdata support.
  3. Add ACPI binding and test the binding.
  4. Add SERIAL_BUS kconfig item.
v4
  1. Convert "uart" bus into "tty_enum" bus.
  2. Convert uart_target_device into tty_slave_device.
  3. Convert uart_board_info into tty_board_info.
  3. Convert kconfig item SERIAL_BUS into TTY_ENUM.
  4. Add automatic device unregister support for slave devices.
     The new tty_port_unregister_device is for this purpose.
  5. Add HID/CID list support.
  6. Convert TTY slave device name into ACPI node name.

All of the codes can be tested by the dummy 8250 device.


Lv Zheng (3):
  TTY: Add TTY slave enumeration support.
  ACPI / UART: Add ACPI enumeration support for UART.
  UART: Add dummy devices to test the enumeration.

 drivers/acpi/Kconfig                 |    6 +
 drivers/acpi/Makefile                |    1 +
 drivers/acpi/acpi_uart.c             |  215 ++++++++++++++++++++
 drivers/acpi/scan.c                  |    1 +
 drivers/tty/Kconfig                  |    3 +
 drivers/tty/Makefile                 |    1 +
 drivers/tty/serial/8250/8250.c       |    5 +-
 drivers/tty/serial/8250/8250_dummy.c |  103 ++++++++++
 drivers/tty/serial/8250/Kconfig      |   10 +
 drivers/tty/serial/8250/Makefile     |    1 +
 drivers/tty/serial/Kconfig           |    1 +
 drivers/tty/serial/serial_core.c     |    2 +-
 drivers/tty/tty_enum.c               |  357 ++++++++++++++++++++++++++++++++++
 drivers/tty/tty_port.c               |   42 +++-
 include/linux/mod_devicetable.h      |    6 +
 include/linux/serial_8250.h          |    4 +
 include/linux/serial_core.h          |   12 ++
 include/linux/tty.h                  |  112 +++++++++++
 18 files changed, 875 insertions(+), 7 deletions(-)
 create mode 100644 drivers/acpi/acpi_uart.c
 create mode 100644 drivers/tty/serial/8250/8250_dummy.c
 create mode 100644 drivers/tty/tty_enum.c

-- 
1.7.10


^ permalink raw reply	[flat|nested] 71+ messages in thread

* [RFC PATCH v4 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART.
  2012-12-03  3:39 [RFC PATCH 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
                   ` (5 preceding siblings ...)
  2013-01-09  9:17 ` [RFC PATCH v4 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
@ 2013-01-09  9:17 ` Lv Zheng
  2013-01-24 10:30 ` [PATCH v5 0/2] ACPI/UART: Add ACPI 5.0 enumeration " Lv Zheng
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 71+ messages in thread
From: Lv Zheng @ 2013-01-09  9:17 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox,
	Mika Westerberg
  Cc: linux-acpi, linux-serial, Lv Zheng

ACPI 5.0 specification introduces enumeration support for SPB buses. This
patch set adds the UART serial bus enumeration support to Linux using such
mechanism.
NOTE: PATCH 3 is a test patch, should not be merged into any published
      Linux source tree.
This patch set is based on the tty tree/tty-next branch.

History:
v1:
  1. Add "uart" bus subsystem.
  2. Add physical UART target device and description
     (uart_device/uart_board_info).
  3. Add logical UART host adapter (klist).
  4. Add UART target device attributes (tty_dev/tty_attrs/modem_lines).
  5. Create tty_device<->uart_device links (host_node/target_node).
v2.
  1. Change UART layer related stuff to non-UART related.
  2. Modify the order of the function parameters.
v3:
  1. Add comments for the uart_board_info and the uart_device.
  2. Add platform_data/archdata support.
  3. Add ACPI binding and test the binding.
  4. Add SERIAL_BUS kconfig item.
v4
  1. Convert "uart" bus into "tty_enum" bus.
  2. Convert uart_target_device into tty_slave_device.
  3. Convert uart_board_info into tty_board_info.
  3. Convert kconfig item SERIAL_BUS into TTY_ENUM.
  4. Add automatic device unregister support for slave devices.
     The new tty_port_unregister_device is for this purpose.
  5. Add HID/CID list support.
  6. Convert TTY slave device name into ACPI node name.

All of the codes can be tested by the dummy 8250 device.


Lv Zheng (3):
  TTY: Add TTY slave enumeration support.
  ACPI / UART: Add ACPI enumeration support for UART.
  UART: Add dummy devices to test the enumeration.

 drivers/acpi/Kconfig                 |    6 +
 drivers/acpi/Makefile                |    1 +
 drivers/acpi/acpi_uart.c             |  215 ++++++++++++++++++++
 drivers/acpi/scan.c                  |    1 +
 drivers/tty/Kconfig                  |    3 +
 drivers/tty/Makefile                 |    1 +
 drivers/tty/serial/8250/8250.c       |    5 +-
 drivers/tty/serial/8250/8250_dummy.c |  103 ++++++++++
 drivers/tty/serial/8250/Kconfig      |   10 +
 drivers/tty/serial/8250/Makefile     |    1 +
 drivers/tty/serial/Kconfig           |    1 +
 drivers/tty/serial/serial_core.c     |    2 +-
 drivers/tty/tty_enum.c               |  357 ++++++++++++++++++++++++++++++++++
 drivers/tty/tty_port.c               |   42 +++-
 include/linux/mod_devicetable.h      |    6 +
 include/linux/serial_8250.h          |    4 +
 include/linux/serial_core.h          |   12 ++
 include/linux/tty.h                  |  112 +++++++++++
 18 files changed, 875 insertions(+), 7 deletions(-)
 create mode 100644 drivers/acpi/acpi_uart.c
 create mode 100644 drivers/tty/serial/8250/8250_dummy.c
 create mode 100644 drivers/tty/tty_enum.c

-- 
1.7.10


^ permalink raw reply	[flat|nested] 71+ messages in thread

* [RFC PATCH v4 2/3] ACPI / UART: Add ACPI enumeration support for UART.
  2013-01-09  9:17 ` [RFC PATCH v4 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
  2013-01-09  9:17   ` [RFC PATCH v4 1/3] TTY: Add TTY slave enumeration support Lv Zheng
  2013-01-09  9:17   ` Lv Zheng
@ 2013-01-09  9:18   ` Lv Zheng
  2013-01-09  9:18   ` Lv Zheng
                     ` (2 subsequent siblings)
  5 siblings, 0 replies; 71+ messages in thread
From: Lv Zheng @ 2013-01-09  9:18 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox,
	Mika Westerberg
  Cc: linux-acpi, linux-serial, Lv Zheng

ACPI 5.0 specification introduces mechanisms of enumerating the slave
devices connected on the serial buses.
This patch follows the specification, implementing such UART enumeration
machanism for Linux.
In order to use this UART device enumeration mechanism, driver writers
are required to call the following API:
Call acpi_tty_register_devices _after_ the creation of the tty ports:
   tty_port_register_device(port, driver, i, parent);
   acpi_uart_register_devices(port);
Where:
   port: the registered tty port.
   parent: the physical device of the UART ports.
   driver and index: required parameters for tty_port_register_device.
In this patch, only SERIAL_CORE drivers are enabled to use this ACPI
UART enumeration mechanism. This can be changed if there are needs
updated in the future.

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/acpi/Kconfig     |    6 ++
 drivers/acpi/Makefile    |    1 +
 drivers/acpi/acpi_uart.c |  215 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/tty/tty_enum.c   |    1 +
 include/linux/tty.h      |    8 ++
 5 files changed, 231 insertions(+)
 create mode 100644 drivers/acpi/acpi_uart.c

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 38c5078..98e2d4e 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -187,6 +187,12 @@ config ACPI_I2C
 	help
 	  ACPI I2C enumeration support.
 
+config ACPI_UART
+	def_tristate TTY_ENUM
+	depends on TTY_ENUM
+	help
+	  ACPI UART enumeration support.
+
 config ACPI_PROCESSOR
 	tristate "Processor"
 	select THERMAL
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 2a4502b..784f332 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -71,6 +71,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS)	+= ec_sys.o
 obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
 obj-$(CONFIG_ACPI_BGRT)		+= bgrt.o
 obj-$(CONFIG_ACPI_I2C)		+= acpi_i2c.o
+obj-$(CONFIG_ACPI_UART)		+= acpi_uart.o
 
 # processor has its own "processor." module_param namespace
 processor-y			:= processor_driver.o processor_throttling.o
diff --git a/drivers/acpi/acpi_uart.c b/drivers/acpi/acpi_uart.c
new file mode 100644
index 0000000..6b1842a
--- /dev/null
+++ b/drivers/acpi/acpi_uart.c
@@ -0,0 +1,215 @@
+/*
+ * acpi_uart.c - ACPI UART enumeration support
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ */
+
+#include <linux/init.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <acpi/acpi.h>
+#include <acpi/acpi_bus.h>
+
+
+static int acpi_uart_add_resources(struct acpi_resource *ares, void *context)
+{
+	struct tty_board_info *info = context;
+
+	if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
+		struct acpi_resource_uart_serialbus *sb;
+
+		sb = &ares->data.uart_serial_bus;
+		if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_UART)
+			return 1;
+
+		/* baud rate */
+		info->baud = sb->default_baud_rate;
+
+		/* data bits */
+		info->cflag &= ~CSIZE;
+		switch (sb->data_bits) {
+		case ACPI_UART_5_DATA_BITS:
+			info->cflag |= CS5;
+			break;
+		case ACPI_UART_6_DATA_BITS:
+			info->cflag |= CS6;
+			break;
+		case ACPI_UART_7_DATA_BITS:
+			info->cflag |= CS7;
+			break;
+		case ACPI_UART_8_DATA_BITS:
+		default:
+			info->cflag |= CS8;
+			break;
+		}
+
+		/* parity */
+		info->cflag &= ~(PARENB | PARODD);
+		if (sb->parity == ACPI_UART_PARITY_EVEN)
+			info->cflag |= PARENB;
+		else if (sb->parity == ACPI_UART_PARITY_ODD)
+			info->cflag |= (PARENB | PARODD);
+
+		/* stop bits */
+		if (sb->stop_bits == ACPI_UART_2_STOP_BITS)
+			info->cflag |= CSTOPB;
+		else
+			info->cflag &= ~CSTOPB;
+
+		/* HW control */
+		if (sb->flow_control & ACPI_UART_FLOW_CONTROL_HW)
+			info->cflag |= CRTSCTS;
+		else
+			info->cflag &= ~CRTSCTS;
+
+		/* SW control */
+		if (sb->flow_control & ACPI_UART_FLOW_CONTROL_XON_XOFF)
+			info->iflag |= (IXON | IXOFF);
+		else
+			info->iflag &= ~(IXON|IXOFF|IXANY);
+
+		/* endianess */
+		if (sb->endian == ACPI_UART_LITTLE_ENDIAN)
+			info->mctrl |= TIOCM_LE;
+		else
+			info->mctrl &= ~TIOCM_LE;
+
+		/* terminal lines */
+		if (sb->lines_enabled & ACPI_UART_DATA_TERMINAL_READY)
+			info->mctrl |= TIOCM_DTR;
+		else
+			info->mctrl &= ~TIOCM_DTR;
+		if (sb->lines_enabled & ACPI_UART_REQUEST_TO_SEND)
+			info->mctrl |= TIOCM_RTS;
+		else
+			info->mctrl &= ~TIOCM_RTS;
+
+		/* modem lines */
+		if (sb->lines_enabled & ACPI_UART_CLEAR_TO_SEND)
+			info->mctrl |= TIOCM_CTS;
+		else
+			info->mctrl &= ~TIOCM_CTS;
+		if (sb->lines_enabled & ACPI_UART_CARRIER_DETECT)
+			info->mctrl |= TIOCM_CAR;
+		else
+			info->mctrl &= ~TIOCM_CAR;
+		if (sb->lines_enabled & ACPI_UART_RING_INDICATOR)
+			info->mctrl |= TIOCM_RNG;
+		else
+			info->mctrl &= ~TIOCM_RNG;
+		if (sb->lines_enabled & ACPI_UART_DATA_SET_READY)
+			info->mctrl |= TIOCM_DSR;
+		else
+			info->mctrl &= ~TIOCM_DSR;
+	} else if (info->irq < 0) {
+		struct resource r;
+
+		if (acpi_dev_resource_interrupt(ares, 0, &r))
+			info->irq = r.start;
+	}
+
+	return 1;
+}
+
+static acpi_status acpi_uart_add_device(acpi_handle handle, u32 level,
+					void *context, void **return_value)
+{
+	struct tty_port *port = context;
+	struct tty_board_info *board_info;
+	struct acpi_device *adev;
+	struct list_head resource_list;
+	int ret;
+	struct tty_slave *tts;
+	int nr_ids, i;
+	size_t info_size;
+	struct acpi_hardware_id *id;
+	char node_name[5];
+	struct acpi_buffer buffer = { sizeof(node_name), node_name };
+
+	if (acpi_bus_get_device(handle, &adev))
+		return AE_OK;
+	if (acpi_bus_get_status(adev) || !adev->status.present)
+		return AE_OK;
+
+	/* Allocate board info structure. */
+	nr_ids = 0;
+	list_for_each_entry(id, &adev->pnp.ids, list)
+		nr_ids++;
+	info_size = sizeof(struct tty_board_info) + (TTY_NAME_SIZE * nr_ids);
+	board_info = kzalloc(info_size, GFP_KERNEL);
+	if (!board_info)
+		return AE_OK;
+
+	/* Enumerate resources. */
+	board_info->acpi_node.handle = handle;
+	board_info->irq = -1;
+	INIT_LIST_HEAD(&resource_list);
+	ret = acpi_dev_get_resources(adev, &resource_list,
+				     acpi_uart_add_resources, board_info);
+	acpi_dev_free_resource_list(&resource_list);
+	if (ret < 0 || !board_info->baud)
+		goto fail;
+
+	/* Use ACPI node name as device name. */
+	acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer);
+	strlcpy(board_info->type, node_name, TTY_NAME_SIZE);
+
+	/* Store PnP IDs. */
+	i = 0;
+	list_for_each_entry(id, &adev->pnp.ids, list) {
+		if (i >= nr_ids)
+			break;
+		strlcpy(board_info->ids[i], id->id, TTY_NAME_SIZE);
+		i++;
+	}
+	board_info->nr_ids = nr_ids;
+
+	tts = tty_bus_register_slave(port, board_info);
+	if (!tts)
+		dev_err(&adev->dev, "failed to add %s UART device from ACPI\n",
+			dev_name(&adev->dev));
+
+fail:
+	kfree(board_info);
+	return AE_OK;
+}
+
+static struct device *acpi_uart_port_parent(struct tty_port *port)
+{
+	return port->dev ? port->dev->parent : NULL;
+}
+
+/**
+ * acpi_uart_register_devices - register the uart slave devices behind the
+ *                              tty port
+ * @port: the host tty port
+ *
+ * Enumerate all tty slave devices behind the host device by walking the
+ * ACPI namespace. When a device is found, it will be added to the Linux
+ * device model and bound to the corresponding ACPI handle.
+ */
+void acpi_uart_register_devices(struct tty_port *port)
+{
+	acpi_handle handle;
+	acpi_status status;
+	struct device *dev;
+
+	dev = acpi_uart_port_parent(port);
+	if (!dev)
+		return;
+	handle = ACPI_HANDLE(dev);
+	if (!handle)
+		return;
+
+	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
+				     acpi_uart_add_device, NULL, port, NULL);
+	if (ACPI_FAILURE(status))
+		dev_warn(dev, "failed to enumerate UART slaves\n");
+}
+EXPORT_SYMBOL_GPL(acpi_uart_register_devices);
diff --git a/drivers/tty/tty_enum.c b/drivers/tty/tty_enum.c
index 2e91cb6..5081dff 100644
--- a/drivers/tty/tty_enum.c
+++ b/drivers/tty/tty_enum.c
@@ -260,6 +260,7 @@ struct tty_slave *tty_bus_register_slave(struct tty_port *port,
 	tts->dev.parent = dev;
 	tts->dev.bus = &tty_enum_bus;
 	tts->dev.type = &tty_slave_type;
+	ACPI_HANDLE_SET(&tts->dev, info->acpi_node.handle);
 
 	tts->dev.platform_data = info->platform_data;
 	if (info->archdata)
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 36eb9d1..c32d16c 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -172,6 +172,7 @@ struct tty_board_info;
  * @irq: stored in tty_slave.irq
  * @platform_data: stored in tty_slave.dev.platform_data
  * @archdata: copied into tty_slave.dev.archdata
+ * @acpi_node: ACPI device node
  * @nr_ids: number of IDs
  * @ids: ID strings
  *
@@ -189,6 +190,7 @@ struct tty_board_info {
 	int irq;
 	void *platform_data;
 	struct dev_archdata *archdata;
+	struct acpi_dev_node acpi_node;
 
 	int nr_ids;
 	/* This must be the last member of tty_board_info */
@@ -741,4 +743,10 @@ do {									\
 } while (0)
 
 
+#if IS_ENABLED(CONFIG_ACPI_UART)
+void acpi_uart_register_devices(struct tty_port *port);
+#else
+static inline void acpi_uart_register_devices(struct tty_port *port) {}
+#endif
+
 #endif
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [RFC PATCH v4 2/3] ACPI / UART: Add ACPI enumeration support for UART.
  2013-01-09  9:17 ` [RFC PATCH v4 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
                     ` (2 preceding siblings ...)
  2013-01-09  9:18   ` [RFC PATCH v4 2/3] ACPI / UART: Add ACPI enumeration support for UART Lv Zheng
@ 2013-01-09  9:18   ` Lv Zheng
  2013-01-09  9:18   ` [RFC PATCH v4 3/3] UART: Add dummy devices to test the enumeration Lv Zheng
  2013-01-09  9:18   ` Lv Zheng
  5 siblings, 0 replies; 71+ messages in thread
From: Lv Zheng @ 2013-01-09  9:18 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox,
	Mika Westerberg
  Cc: linux-acpi, linux-serial, Lv Zheng

ACPI 5.0 specification introduces mechanisms of enumerating the slave
devices connected on the serial buses.
This patch follows the specification, implementing such UART enumeration
machanism for Linux.
In order to use this UART device enumeration mechanism, driver writers
are required to call the following API:
Call acpi_tty_register_devices _after_ the creation of the tty ports:
   tty_port_register_device(port, driver, i, parent);
   acpi_uart_register_devices(port);
Where:
   port: the registered tty port.
   parent: the physical device of the UART ports.
   driver and index: required parameters for tty_port_register_device.
In this patch, only SERIAL_CORE drivers are enabled to use this ACPI
UART enumeration mechanism. This can be changed if there are needs
updated in the future.

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/acpi/Kconfig     |    6 ++
 drivers/acpi/Makefile    |    1 +
 drivers/acpi/acpi_uart.c |  215 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/tty/tty_enum.c   |    1 +
 include/linux/tty.h      |    8 ++
 5 files changed, 231 insertions(+)
 create mode 100644 drivers/acpi/acpi_uart.c

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 38c5078..98e2d4e 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -187,6 +187,12 @@ config ACPI_I2C
 	help
 	  ACPI I2C enumeration support.
 
+config ACPI_UART
+	def_tristate TTY_ENUM
+	depends on TTY_ENUM
+	help
+	  ACPI UART enumeration support.
+
 config ACPI_PROCESSOR
 	tristate "Processor"
 	select THERMAL
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 2a4502b..784f332 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -71,6 +71,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS)	+= ec_sys.o
 obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
 obj-$(CONFIG_ACPI_BGRT)		+= bgrt.o
 obj-$(CONFIG_ACPI_I2C)		+= acpi_i2c.o
+obj-$(CONFIG_ACPI_UART)		+= acpi_uart.o
 
 # processor has its own "processor." module_param namespace
 processor-y			:= processor_driver.o processor_throttling.o
diff --git a/drivers/acpi/acpi_uart.c b/drivers/acpi/acpi_uart.c
new file mode 100644
index 0000000..6b1842a
--- /dev/null
+++ b/drivers/acpi/acpi_uart.c
@@ -0,0 +1,215 @@
+/*
+ * acpi_uart.c - ACPI UART enumeration support
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ */
+
+#include <linux/init.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <acpi/acpi.h>
+#include <acpi/acpi_bus.h>
+
+
+static int acpi_uart_add_resources(struct acpi_resource *ares, void *context)
+{
+	struct tty_board_info *info = context;
+
+	if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
+		struct acpi_resource_uart_serialbus *sb;
+
+		sb = &ares->data.uart_serial_bus;
+		if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_UART)
+			return 1;
+
+		/* baud rate */
+		info->baud = sb->default_baud_rate;
+
+		/* data bits */
+		info->cflag &= ~CSIZE;
+		switch (sb->data_bits) {
+		case ACPI_UART_5_DATA_BITS:
+			info->cflag |= CS5;
+			break;
+		case ACPI_UART_6_DATA_BITS:
+			info->cflag |= CS6;
+			break;
+		case ACPI_UART_7_DATA_BITS:
+			info->cflag |= CS7;
+			break;
+		case ACPI_UART_8_DATA_BITS:
+		default:
+			info->cflag |= CS8;
+			break;
+		}
+
+		/* parity */
+		info->cflag &= ~(PARENB | PARODD);
+		if (sb->parity == ACPI_UART_PARITY_EVEN)
+			info->cflag |= PARENB;
+		else if (sb->parity == ACPI_UART_PARITY_ODD)
+			info->cflag |= (PARENB | PARODD);
+
+		/* stop bits */
+		if (sb->stop_bits == ACPI_UART_2_STOP_BITS)
+			info->cflag |= CSTOPB;
+		else
+			info->cflag &= ~CSTOPB;
+
+		/* HW control */
+		if (sb->flow_control & ACPI_UART_FLOW_CONTROL_HW)
+			info->cflag |= CRTSCTS;
+		else
+			info->cflag &= ~CRTSCTS;
+
+		/* SW control */
+		if (sb->flow_control & ACPI_UART_FLOW_CONTROL_XON_XOFF)
+			info->iflag |= (IXON | IXOFF);
+		else
+			info->iflag &= ~(IXON|IXOFF|IXANY);
+
+		/* endianess */
+		if (sb->endian == ACPI_UART_LITTLE_ENDIAN)
+			info->mctrl |= TIOCM_LE;
+		else
+			info->mctrl &= ~TIOCM_LE;
+
+		/* terminal lines */
+		if (sb->lines_enabled & ACPI_UART_DATA_TERMINAL_READY)
+			info->mctrl |= TIOCM_DTR;
+		else
+			info->mctrl &= ~TIOCM_DTR;
+		if (sb->lines_enabled & ACPI_UART_REQUEST_TO_SEND)
+			info->mctrl |= TIOCM_RTS;
+		else
+			info->mctrl &= ~TIOCM_RTS;
+
+		/* modem lines */
+		if (sb->lines_enabled & ACPI_UART_CLEAR_TO_SEND)
+			info->mctrl |= TIOCM_CTS;
+		else
+			info->mctrl &= ~TIOCM_CTS;
+		if (sb->lines_enabled & ACPI_UART_CARRIER_DETECT)
+			info->mctrl |= TIOCM_CAR;
+		else
+			info->mctrl &= ~TIOCM_CAR;
+		if (sb->lines_enabled & ACPI_UART_RING_INDICATOR)
+			info->mctrl |= TIOCM_RNG;
+		else
+			info->mctrl &= ~TIOCM_RNG;
+		if (sb->lines_enabled & ACPI_UART_DATA_SET_READY)
+			info->mctrl |= TIOCM_DSR;
+		else
+			info->mctrl &= ~TIOCM_DSR;
+	} else if (info->irq < 0) {
+		struct resource r;
+
+		if (acpi_dev_resource_interrupt(ares, 0, &r))
+			info->irq = r.start;
+	}
+
+	return 1;
+}
+
+static acpi_status acpi_uart_add_device(acpi_handle handle, u32 level,
+					void *context, void **return_value)
+{
+	struct tty_port *port = context;
+	struct tty_board_info *board_info;
+	struct acpi_device *adev;
+	struct list_head resource_list;
+	int ret;
+	struct tty_slave *tts;
+	int nr_ids, i;
+	size_t info_size;
+	struct acpi_hardware_id *id;
+	char node_name[5];
+	struct acpi_buffer buffer = { sizeof(node_name), node_name };
+
+	if (acpi_bus_get_device(handle, &adev))
+		return AE_OK;
+	if (acpi_bus_get_status(adev) || !adev->status.present)
+		return AE_OK;
+
+	/* Allocate board info structure. */
+	nr_ids = 0;
+	list_for_each_entry(id, &adev->pnp.ids, list)
+		nr_ids++;
+	info_size = sizeof(struct tty_board_info) + (TTY_NAME_SIZE * nr_ids);
+	board_info = kzalloc(info_size, GFP_KERNEL);
+	if (!board_info)
+		return AE_OK;
+
+	/* Enumerate resources. */
+	board_info->acpi_node.handle = handle;
+	board_info->irq = -1;
+	INIT_LIST_HEAD(&resource_list);
+	ret = acpi_dev_get_resources(adev, &resource_list,
+				     acpi_uart_add_resources, board_info);
+	acpi_dev_free_resource_list(&resource_list);
+	if (ret < 0 || !board_info->baud)
+		goto fail;
+
+	/* Use ACPI node name as device name. */
+	acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer);
+	strlcpy(board_info->type, node_name, TTY_NAME_SIZE);
+
+	/* Store PnP IDs. */
+	i = 0;
+	list_for_each_entry(id, &adev->pnp.ids, list) {
+		if (i >= nr_ids)
+			break;
+		strlcpy(board_info->ids[i], id->id, TTY_NAME_SIZE);
+		i++;
+	}
+	board_info->nr_ids = nr_ids;
+
+	tts = tty_bus_register_slave(port, board_info);
+	if (!tts)
+		dev_err(&adev->dev, "failed to add %s UART device from ACPI\n",
+			dev_name(&adev->dev));
+
+fail:
+	kfree(board_info);
+	return AE_OK;
+}
+
+static struct device *acpi_uart_port_parent(struct tty_port *port)
+{
+	return port->dev ? port->dev->parent : NULL;
+}
+
+/**
+ * acpi_uart_register_devices - register the uart slave devices behind the
+ *                              tty port
+ * @port: the host tty port
+ *
+ * Enumerate all tty slave devices behind the host device by walking the
+ * ACPI namespace. When a device is found, it will be added to the Linux
+ * device model and bound to the corresponding ACPI handle.
+ */
+void acpi_uart_register_devices(struct tty_port *port)
+{
+	acpi_handle handle;
+	acpi_status status;
+	struct device *dev;
+
+	dev = acpi_uart_port_parent(port);
+	if (!dev)
+		return;
+	handle = ACPI_HANDLE(dev);
+	if (!handle)
+		return;
+
+	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
+				     acpi_uart_add_device, NULL, port, NULL);
+	if (ACPI_FAILURE(status))
+		dev_warn(dev, "failed to enumerate UART slaves\n");
+}
+EXPORT_SYMBOL_GPL(acpi_uart_register_devices);
diff --git a/drivers/tty/tty_enum.c b/drivers/tty/tty_enum.c
index 2e91cb6..5081dff 100644
--- a/drivers/tty/tty_enum.c
+++ b/drivers/tty/tty_enum.c
@@ -260,6 +260,7 @@ struct tty_slave *tty_bus_register_slave(struct tty_port *port,
 	tts->dev.parent = dev;
 	tts->dev.bus = &tty_enum_bus;
 	tts->dev.type = &tty_slave_type;
+	ACPI_HANDLE_SET(&tts->dev, info->acpi_node.handle);
 
 	tts->dev.platform_data = info->platform_data;
 	if (info->archdata)
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 36eb9d1..c32d16c 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -172,6 +172,7 @@ struct tty_board_info;
  * @irq: stored in tty_slave.irq
  * @platform_data: stored in tty_slave.dev.platform_data
  * @archdata: copied into tty_slave.dev.archdata
+ * @acpi_node: ACPI device node
  * @nr_ids: number of IDs
  * @ids: ID strings
  *
@@ -189,6 +190,7 @@ struct tty_board_info {
 	int irq;
 	void *platform_data;
 	struct dev_archdata *archdata;
+	struct acpi_dev_node acpi_node;
 
 	int nr_ids;
 	/* This must be the last member of tty_board_info */
@@ -741,4 +743,10 @@ do {									\
 } while (0)
 
 
+#if IS_ENABLED(CONFIG_ACPI_UART)
+void acpi_uart_register_devices(struct tty_port *port);
+#else
+static inline void acpi_uart_register_devices(struct tty_port *port) {}
+#endif
+
 #endif
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [RFC PATCH v4 3/3] UART: Add dummy devices to test the enumeration.
  2013-01-09  9:17 ` [RFC PATCH v4 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
                     ` (3 preceding siblings ...)
  2013-01-09  9:18   ` Lv Zheng
@ 2013-01-09  9:18   ` Lv Zheng
  2013-01-09  9:18   ` Lv Zheng
  5 siblings, 0 replies; 71+ messages in thread
From: Lv Zheng @ 2013-01-09  9:18 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox,
	Mika Westerberg
  Cc: linux-acpi, linux-serial, Lv Zheng

This is a test patch that should not be merged into any of the published
Linux source tree.

The test is based on the following customized DSDT (containing the dummy
uart host adapter INTF000 and target device INTF001):

1. customized DSDT:
  Device (UA00)
  {
      Name (_HID, "INTF000")  // _HID: Hardware ID
      Name (RBUF, ResourceTemplate ()
      {
          Memory32Fixed (ReadWrite,
              0x00000000,         // Address Base
              0x00001000)         // Address Length
      })
      Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
      {
          Return (RBUF)
      }
      Method (_STA, 0, NotSerialized)  // _STA: Status
      {
          Return (0x0F)
      }
      Device (BTH0)
      {
          Name (_HID, "INTF001")  // _HID: Hardware ID
          Name (_CID, "PNPF001")  // _CID: Compatible ID
          Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
          {
              Name (UBUF, ResourceTemplate ()
              {
                  UartSerialBus (0x0001C200, DataBitsEight, StopBitsOne,
                      0xC0, LittleEndian, ParityTypeNone, FlowControlHardware,
                      0x0020, 0x0020, "\\_SB.PCI0.UA00",
                      0x00, ResourceConsumer, ,
                      )
              })
              Return (UBUF)
          }
          Method (_STA, 0, NotSerialized)  // _STA: Status
          {
              Return (0x0F)
          }
      }
  }

2. uevent monitor:
# udevadm monitor --kernel --environment > ~/uart.uevents
# echo add > /sys/class/tty/uevent
# echo add > /sys/class/tty/devices/BTH0:00/uevent
# cat ~/uart.uevents
monitor will print the received events for:
KERNEL - the kernel uevent

KERNEL[252.443458] add      /bus/tty_enum (bus)
ACTION=add
DEVPATH=/bus/tty_enum
SEQNUM=1142
SUBSYSTEM=bus

KERNEL[268.491709] add      /devices/platform/INTF000:00/tty/ttyS0/BTH0:00 (tty)
ACTION=add
DEVPATH=/devices/platform/INTF000:00/tty/ttyS0/BTH0:00
DEVTYPE=tty_slave
MODALIAS=tty::INTF001:PNPF001:
SEQNUM=1144
SUBSYSTEM=tty

3. kobjects attributes and links:
# cat /sys/bus/tty_enum/devices/BTH0:00/modalias
tty::INTF001:PNPF001:
# cat /sys/bus/tty_enum/devices/BTH0:00/tty_attrs
115200 8N0 HW
# cat /sys/bus/tty_enum/devices/BTH0:00/modem_lines
LE:RTS,CTS,

# ls -l /sys/bus/tty_enum/devices/
BTH0:00 -> ../../../devices/platform/INTF000:00/tty/ttyS0/tty/BTH0:00
# ls -l /sys/devices/platform/INTF000:00/tty/ttyS0/BTH0:00/
firmware_node -> ../../../../../LNXSYSTM:00/device:00/INTF000:00/INTF001:00
subsystem -> ../../../../../../bus/tty_enum
# ls -l /sys/bus/acpi/devices/INTF001:00/
physical_node -> ../../../../pnp0/00:00
physical_node1 -> ../../../../platform/INTF000:00/tty/ttyS0/BTH0:00

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/acpi/scan.c                  |    1 +
 drivers/tty/serial/8250/8250.c       |    5 +-
 drivers/tty/serial/8250/8250_dummy.c |  103 ++++++++++++++++++++++++++++++++++
 drivers/tty/serial/8250/Kconfig      |   10 ++++
 drivers/tty/serial/8250/Makefile     |    1 +
 include/linux/serial_8250.h          |    4 ++
 include/linux/serial_core.h          |   12 ++++
 7 files changed, 133 insertions(+), 3 deletions(-)
 create mode 100644 drivers/tty/serial/8250/8250_dummy.c

diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 53502d1..56611f3 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -36,6 +36,7 @@ static const char *dummy_hid = "device";
 static const struct acpi_device_id acpi_platform_device_ids[] = {
 
 	{ "PNP0D40" },
+	{ "INTF000" },
 
 	/* Haswell LPSS devices */
 	{ "INT33C0", 0 },
diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c
index d085e3a..32dfad4 100644
--- a/drivers/tty/serial/8250/8250.c
+++ b/drivers/tty/serial/8250/8250.c
@@ -56,8 +56,6 @@ static unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
 
 static unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS;
 
-static struct uart_driver serial8250_reg;
-
 static int serial_index(struct uart_port *port)
 {
 	return (serial8250_reg.minor - 64) + port->line;
@@ -2978,7 +2976,7 @@ int serial8250_find_port(struct uart_port *p)
 #define SERIAL8250_CONSOLE	NULL
 #endif
 
-static struct uart_driver serial8250_reg = {
+struct uart_driver serial8250_reg = {
 	.owner			= THIS_MODULE,
 	.driver_name		= "serial",
 	.dev_name		= "ttyS",
@@ -2986,6 +2984,7 @@ static struct uart_driver serial8250_reg = {
 	.minor			= 64,
 	.cons			= SERIAL8250_CONSOLE,
 };
+EXPORT_SYMBOL_GPL(serial8250_reg);
 
 /*
  * early_serial_setup - early registration for 8250 ports
diff --git a/drivers/tty/serial/8250/8250_dummy.c b/drivers/tty/serial/8250/8250_dummy.c
new file mode 100644
index 0000000..e5aadeb
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_dummy.c
@@ -0,0 +1,103 @@
+/*
+ * 8250_dummy.c: Dummy 8250 UART target device enumerator
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/serial_8250.h>
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct dummy8250_data {
+	int	last_lcr;
+	int	line;
+};
+
+static void dummy8250_serial_out(struct uart_port *p, int offset, int value)
+{
+}
+
+static unsigned int dummy8250_serial_in(struct uart_port *p, int offset)
+{
+	return 0;
+}
+
+static int __devinit dummy8250_probe(struct platform_device *pdev)
+{
+	struct uart_8250_port uart = {};
+	struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	struct dummy8250_data *data;
+
+	if (!regs) {
+		dev_err(&pdev->dev, "no registers defined\n");
+		return -EINVAL;
+	}
+
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+	uart.port.private_data = data;
+
+	spin_lock_init(&uart.port.lock);
+	uart.port.mapbase = regs->start;
+	uart.port.type = PORT_8250;
+	uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP |
+		UPF_FIXED_PORT | UPF_FIXED_TYPE;
+	uart.port.dev = &pdev->dev;
+
+	uart.port.iotype = UPIO_MEM;
+	uart.port.serial_in = dummy8250_serial_in;
+	uart.port.serial_out = dummy8250_serial_out;
+	uart.port.uartclk = 40000000;
+
+	data->line = serial8250_register_8250_port(&uart);
+	if (data->line < 0)
+		return data->line;
+
+#ifdef CONFIG_ACPI_UART
+	acpi_uart_register_devices(serial8250_tty_port(data->line));
+#endif
+	platform_set_drvdata(pdev, data);
+
+	return 0;
+}
+
+static int __devexit dummy8250_remove(struct platform_device *pdev)
+{
+	struct dummy8250_data *data = platform_get_drvdata(pdev);
+
+	serial8250_unregister_port(data->line);
+	return 0;
+}
+
+static const struct acpi_device_id dummy8250_match[] = {
+	{ .id = "INTF000" },
+	{ /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(acpi, dummy8250_match);
+
+static struct platform_driver dummy8250_platform_driver = {
+	.driver = {
+		.name			= "dummy-uart",
+		.owner			= THIS_MODULE,
+		.acpi_match_table	= dummy8250_match,
+	},
+	.probe				= dummy8250_probe,
+	.remove				= __devexit_p(dummy8250_remove),
+};
+
+module_platform_driver(dummy8250_platform_driver);
+
+MODULE_AUTHOR("Lv Zheng");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Dummy 8250 serial port driver");
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index c31133a..3f04247 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -270,6 +270,16 @@ config SERIAL_8250_DW
 	  Selecting this option will enable handling of the extra features
 	  present in the Synopsys DesignWare APB UART.
 
+config SERIAL_8250_DUMMY
+	tristate "Support for dummy ACPI 8250"
+	depends on SERIAL_8250 && ACPI
+	help
+	  Selecting this option will enable a test UART target device DUMMY
+	  under the ISA serial8250 and a test UART host adapter INTF000
+	  as an platform device for the purpose of testing the ACPI UART
+	  enumeration support.
+	  If unsure, say "N" here.
+
 config SERIAL_8250_EM
 	tristate "Support for Emma Mobile integrated serial port"
 	depends on SERIAL_8250 && ARM && HAVE_CLK
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index 108fe7f..fb82aa9 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -19,3 +19,4 @@ obj-$(CONFIG_SERIAL_8250_HUB6)		+= 8250_hub6.o
 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_DUMMY)		+= 8250_dummy.o
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index c490d20..c7aef78 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -96,6 +96,10 @@ struct uart_8250_port {
 	void			(*dl_write)(struct uart_8250_port *, int);
 };
 
+extern struct uart_driver serial8250_reg;
+
+#define serial8250_tty_port(line)	uart_tty_port(&serial8250_reg, (line))
+
 int serial8250_register_8250_port(struct uart_8250_port *);
 void serial8250_unregister_port(int line);
 void serial8250_suspend_port(int line);
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index c6690a2..59f8582 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -246,6 +246,18 @@ struct uart_driver {
 	struct tty_driver	*tty_driver;
 };
 
+static inline struct tty_port *uart_tty_port(struct uart_driver *drv,
+					     unsigned int line)
+{
+	struct uart_state *state;
+
+	if (line >= drv->nr)
+		return NULL;
+
+	state = drv->state + line;
+	return &state->port;
+}
+
 void uart_write_wakeup(struct uart_port *port);
 
 /*
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [RFC PATCH v4 3/3] UART: Add dummy devices to test the enumeration.
  2013-01-09  9:17 ` [RFC PATCH v4 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
                     ` (4 preceding siblings ...)
  2013-01-09  9:18   ` [RFC PATCH v4 3/3] UART: Add dummy devices to test the enumeration Lv Zheng
@ 2013-01-09  9:18   ` Lv Zheng
  5 siblings, 0 replies; 71+ messages in thread
From: Lv Zheng @ 2013-01-09  9:18 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox,
	Mika Westerberg
  Cc: linux-acpi, linux-serial, Lv Zheng

This is a test patch that should not be merged into any of the published
Linux source tree.

The test is based on the following customized DSDT (containing the dummy
uart host adapter INTF000 and target device INTF001):

1. customized DSDT:
  Device (UA00)
  {
      Name (_HID, "INTF000")  // _HID: Hardware ID
      Name (RBUF, ResourceTemplate ()
      {
          Memory32Fixed (ReadWrite,
              0x00000000,         // Address Base
              0x00001000)         // Address Length
      })
      Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
      {
          Return (RBUF)
      }
      Method (_STA, 0, NotSerialized)  // _STA: Status
      {
          Return (0x0F)
      }
      Device (BTH0)
      {
          Name (_HID, "INTF001")  // _HID: Hardware ID
          Name (_CID, "PNPF001")  // _CID: Compatible ID
          Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
          {
              Name (UBUF, ResourceTemplate ()
              {
                  UartSerialBus (0x0001C200, DataBitsEight, StopBitsOne,
                      0xC0, LittleEndian, ParityTypeNone, FlowControlHardware,
                      0x0020, 0x0020, "\\_SB.PCI0.UA00",
                      0x00, ResourceConsumer, ,
                      )
              })
              Return (UBUF)
          }
          Method (_STA, 0, NotSerialized)  // _STA: Status
          {
              Return (0x0F)
          }
      }
  }

2. uevent monitor:
# udevadm monitor --kernel --environment > ~/uart.uevents
# echo add > /sys/class/tty/uevent
# echo add > /sys/class/tty/devices/BTH0:00/uevent
# cat ~/uart.uevents
monitor will print the received events for:
KERNEL - the kernel uevent

KERNEL[252.443458] add      /bus/tty_enum (bus)
ACTION=add
DEVPATH=/bus/tty_enum
SEQNUM=1142
SUBSYSTEM=bus

KERNEL[268.491709] add      /devices/platform/INTF000:00/tty/ttyS0/BTH0:00 (tty)
ACTION=add
DEVPATH=/devices/platform/INTF000:00/tty/ttyS0/BTH0:00
DEVTYPE=tty_slave
MODALIAS=tty::INTF001:PNPF001:
SEQNUM=1144
SUBSYSTEM=tty

3. kobjects attributes and links:
# cat /sys/bus/tty_enum/devices/BTH0:00/modalias
tty::INTF001:PNPF001:
# cat /sys/bus/tty_enum/devices/BTH0:00/tty_attrs
115200 8N0 HW
# cat /sys/bus/tty_enum/devices/BTH0:00/modem_lines
LE:RTS,CTS,

# ls -l /sys/bus/tty_enum/devices/
BTH0:00 -> ../../../devices/platform/INTF000:00/tty/ttyS0/tty/BTH0:00
# ls -l /sys/devices/platform/INTF000:00/tty/ttyS0/BTH0:00/
firmware_node -> ../../../../../LNXSYSTM:00/device:00/INTF000:00/INTF001:00
subsystem -> ../../../../../../bus/tty_enum
# ls -l /sys/bus/acpi/devices/INTF001:00/
physical_node -> ../../../../pnp0/00:00
physical_node1 -> ../../../../platform/INTF000:00/tty/ttyS0/BTH0:00

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/acpi/scan.c                  |    1 +
 drivers/tty/serial/8250/8250.c       |    5 +-
 drivers/tty/serial/8250/8250_dummy.c |  103 ++++++++++++++++++++++++++++++++++
 drivers/tty/serial/8250/Kconfig      |   10 ++++
 drivers/tty/serial/8250/Makefile     |    1 +
 include/linux/serial_8250.h          |    4 ++
 include/linux/serial_core.h          |   12 ++++
 7 files changed, 133 insertions(+), 3 deletions(-)
 create mode 100644 drivers/tty/serial/8250/8250_dummy.c

diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 53502d1..56611f3 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -36,6 +36,7 @@ static const char *dummy_hid = "device";
 static const struct acpi_device_id acpi_platform_device_ids[] = {
 
 	{ "PNP0D40" },
+	{ "INTF000" },
 
 	/* Haswell LPSS devices */
 	{ "INT33C0", 0 },
diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c
index d085e3a..32dfad4 100644
--- a/drivers/tty/serial/8250/8250.c
+++ b/drivers/tty/serial/8250/8250.c
@@ -56,8 +56,6 @@ static unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
 
 static unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS;
 
-static struct uart_driver serial8250_reg;
-
 static int serial_index(struct uart_port *port)
 {
 	return (serial8250_reg.minor - 64) + port->line;
@@ -2978,7 +2976,7 @@ int serial8250_find_port(struct uart_port *p)
 #define SERIAL8250_CONSOLE	NULL
 #endif
 
-static struct uart_driver serial8250_reg = {
+struct uart_driver serial8250_reg = {
 	.owner			= THIS_MODULE,
 	.driver_name		= "serial",
 	.dev_name		= "ttyS",
@@ -2986,6 +2984,7 @@ static struct uart_driver serial8250_reg = {
 	.minor			= 64,
 	.cons			= SERIAL8250_CONSOLE,
 };
+EXPORT_SYMBOL_GPL(serial8250_reg);
 
 /*
  * early_serial_setup - early registration for 8250 ports
diff --git a/drivers/tty/serial/8250/8250_dummy.c b/drivers/tty/serial/8250/8250_dummy.c
new file mode 100644
index 0000000..e5aadeb
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_dummy.c
@@ -0,0 +1,103 @@
+/*
+ * 8250_dummy.c: Dummy 8250 UART target device enumerator
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/serial_8250.h>
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct dummy8250_data {
+	int	last_lcr;
+	int	line;
+};
+
+static void dummy8250_serial_out(struct uart_port *p, int offset, int value)
+{
+}
+
+static unsigned int dummy8250_serial_in(struct uart_port *p, int offset)
+{
+	return 0;
+}
+
+static int __devinit dummy8250_probe(struct platform_device *pdev)
+{
+	struct uart_8250_port uart = {};
+	struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	struct dummy8250_data *data;
+
+	if (!regs) {
+		dev_err(&pdev->dev, "no registers defined\n");
+		return -EINVAL;
+	}
+
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+	uart.port.private_data = data;
+
+	spin_lock_init(&uart.port.lock);
+	uart.port.mapbase = regs->start;
+	uart.port.type = PORT_8250;
+	uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP |
+		UPF_FIXED_PORT | UPF_FIXED_TYPE;
+	uart.port.dev = &pdev->dev;
+
+	uart.port.iotype = UPIO_MEM;
+	uart.port.serial_in = dummy8250_serial_in;
+	uart.port.serial_out = dummy8250_serial_out;
+	uart.port.uartclk = 40000000;
+
+	data->line = serial8250_register_8250_port(&uart);
+	if (data->line < 0)
+		return data->line;
+
+#ifdef CONFIG_ACPI_UART
+	acpi_uart_register_devices(serial8250_tty_port(data->line));
+#endif
+	platform_set_drvdata(pdev, data);
+
+	return 0;
+}
+
+static int __devexit dummy8250_remove(struct platform_device *pdev)
+{
+	struct dummy8250_data *data = platform_get_drvdata(pdev);
+
+	serial8250_unregister_port(data->line);
+	return 0;
+}
+
+static const struct acpi_device_id dummy8250_match[] = {
+	{ .id = "INTF000" },
+	{ /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(acpi, dummy8250_match);
+
+static struct platform_driver dummy8250_platform_driver = {
+	.driver = {
+		.name			= "dummy-uart",
+		.owner			= THIS_MODULE,
+		.acpi_match_table	= dummy8250_match,
+	},
+	.probe				= dummy8250_probe,
+	.remove				= __devexit_p(dummy8250_remove),
+};
+
+module_platform_driver(dummy8250_platform_driver);
+
+MODULE_AUTHOR("Lv Zheng");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Dummy 8250 serial port driver");
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index c31133a..3f04247 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -270,6 +270,16 @@ config SERIAL_8250_DW
 	  Selecting this option will enable handling of the extra features
 	  present in the Synopsys DesignWare APB UART.
 
+config SERIAL_8250_DUMMY
+	tristate "Support for dummy ACPI 8250"
+	depends on SERIAL_8250 && ACPI
+	help
+	  Selecting this option will enable a test UART target device DUMMY
+	  under the ISA serial8250 and a test UART host adapter INTF000
+	  as an platform device for the purpose of testing the ACPI UART
+	  enumeration support.
+	  If unsure, say "N" here.
+
 config SERIAL_8250_EM
 	tristate "Support for Emma Mobile integrated serial port"
 	depends on SERIAL_8250 && ARM && HAVE_CLK
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index 108fe7f..fb82aa9 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -19,3 +19,4 @@ obj-$(CONFIG_SERIAL_8250_HUB6)		+= 8250_hub6.o
 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_DUMMY)		+= 8250_dummy.o
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index c490d20..c7aef78 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -96,6 +96,10 @@ struct uart_8250_port {
 	void			(*dl_write)(struct uart_8250_port *, int);
 };
 
+extern struct uart_driver serial8250_reg;
+
+#define serial8250_tty_port(line)	uart_tty_port(&serial8250_reg, (line))
+
 int serial8250_register_8250_port(struct uart_8250_port *);
 void serial8250_unregister_port(int line);
 void serial8250_suspend_port(int line);
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index c6690a2..59f8582 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -246,6 +246,18 @@ struct uart_driver {
 	struct tty_driver	*tty_driver;
 };
 
+static inline struct tty_port *uart_tty_port(struct uart_driver *drv,
+					     unsigned int line)
+{
+	struct uart_state *state;
+
+	if (line >= drv->nr)
+		return NULL;
+
+	state = drv->state + line;
+	return &state->port;
+}
+
 void uart_write_wakeup(struct uart_port *port);
 
 /*
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v5 0/2] ACPI/UART: Add ACPI 5.0 enumeration support for UART
  2012-12-03  3:39 [RFC PATCH 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
                   ` (6 preceding siblings ...)
  2013-01-09  9:17 ` [RFC PATCH v4 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
@ 2013-01-24 10:30 ` Lv Zheng
  2013-01-24 10:30   ` [PATCH v5 1/2] TTY: Add TTY slave enumeration support Lv Zheng
  2013-01-24 10:30   ` [PATCH v5 2/2] ACPI / UART: Add ACPI enumeration support for UART Lv Zheng
  2013-01-24 10:30 ` [RFC PATCH v5] HACK: UART: Add dummy devices to test the enumeration Lv Zheng
                   ` (2 subsequent siblings)
  10 siblings, 2 replies; 71+ messages in thread
From: Lv Zheng @ 2013-01-24 10:30 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Alan Cox, Rafael J Wysocki, Len Brown,
	Mika Westerberg
  Cc: linux-acpi, linux-serial, Lv Zheng

ACPI 5.0 specification introduces enumeration support for SPB buses. This
patch set adds the UART serial bus enumeration support to Linux using such
mechanism.
This patch set is based on the tty tree/tty-next branch.

History:
v1:
  1. Add "uart" bus subsystem.
  2. Add physical UART target device and description
     (uart_device/uart_board_info).
  3. Add logical UART host adapter (klist).
  4. Add UART target device attributes (tty_dev/tty_attrs/modem_lines).
  5. Create tty_device<->uart_device links (host_node/target_node).
v2.
  1. Change UART layer related stuff to non-UART related.
  2. Modify the order of the function parameters.
v3:
  1. Add comments for the uart_board_info and the uart_device.
  2. Add platform_data/archdata support.
  3. Add ACPI binding and test the binding (in seperate patch).
  4. Add SERIAL_BUS kconfig item.
v4
  1. Convert "uart" bus into "tty_enum" bus.
  2. Convert uart_target_device into tty_slave_device.
  3. Convert uart_board_info into tty_board_info.
  3. Convert kconfig item SERIAL_BUS into TTY_ENUM.
  4. Add automatic device unregister support for slave devices.
     The new tty_port_unregister_device is for this purpose.
  5. Add HID/CID list support.
  6. Convert TTY slave device name into ACPI node name.
v5
  1. Add missing static reported by recent kernel build test.

Lv Zheng (2):
  TTY: Add TTY slave enumeration support
  ACPI / UART: Add ACPI enumeration support for UART

 drivers/acpi/Kconfig             |    6 +
 drivers/acpi/Makefile            |    1 +
 drivers/acpi/acpi_uart.c         |  215 +++++++++++++++++++++++
 drivers/tty/Kconfig              |    3 +
 drivers/tty/Makefile             |    1 +
 drivers/tty/serial/Kconfig       |    1 +
 drivers/tty/serial/serial_core.c |    2 +-
 drivers/tty/tty_enum.c           |  357 ++++++++++++++++++++++++++++++++++++++
 drivers/tty/tty_port.c           |   42 ++++-
 include/linux/mod_devicetable.h  |    6 +
 include/linux/tty.h              |  112 ++++++++++++
 11 files changed, 742 insertions(+), 4 deletions(-)
 create mode 100644 drivers/acpi/acpi_uart.c
 create mode 100644 drivers/tty/tty_enum.c

-- 
1.7.10


^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH v5 1/2] TTY: Add TTY slave enumeration support
  2013-01-24 10:30 ` [PATCH v5 0/2] ACPI/UART: Add ACPI 5.0 enumeration " Lv Zheng
@ 2013-01-24 10:30   ` Lv Zheng
  2013-01-25 21:41     ` Greg Kroah-Hartman
  2013-01-25 21:45     ` Greg Kroah-Hartman
  2013-01-24 10:30   ` [PATCH v5 2/2] ACPI / UART: Add ACPI enumeration support for UART Lv Zheng
  1 sibling, 2 replies; 71+ messages in thread
From: Lv Zheng @ 2013-01-24 10:30 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Alan Cox, Rafael J Wysocki, Len Brown,
	Mika Westerberg
  Cc: linux-acpi, linux-serial, Lv Zheng

In the recent ACPI 5.0 specification updates, firmwares are provided the
possibilities to enumerate the UART slave devices known to the platform
vendors.
There are the needs in Linux to utilize the benefits:
1. hotplug uevent
2. serial configuration
Currently, only serial cards on the specific bus (ex. PCMCIA) can be
enumerated and userspace can obtain the hotplug event of the UART target
devices. Linux kernel is lack of an overall enumeration mechanism for
UART slave devices.
In order to send uevent, a device need to be a class device or a bus
device. This patch introduces a tty_enum bus since the enumerated slave
devices are expected to be physical devices.
When the UART slave devices are created, userspace uevent rules can
pass the creation details to the userspace driver managers
(ex. hciattach), then the device managers can read hardware IDs and the
serial configurations from the exported device attributes to match and
configure a userspace TTY device driver.
The created slave devices will be automatically unregistered when the
associated TTY ports are destructed.

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
Tested-by: Fengguang Wu <fengguang.wu@intel.com>
---
History:
v5
 1. Build/boot test of this patch relies on Fengguang's 0-day kernel
    tests.
---
 drivers/tty/Kconfig              |    3 +
 drivers/tty/Makefile             |    1 +
 drivers/tty/serial/Kconfig       |    1 +
 drivers/tty/serial/serial_core.c |    2 +-
 drivers/tty/tty_enum.c           |  356 ++++++++++++++++++++++++++++++++++++++
 drivers/tty/tty_port.c           |   42 ++++-
 include/linux/mod_devicetable.h  |    6 +
 include/linux/tty.h              |  104 +++++++++++
 8 files changed, 511 insertions(+), 4 deletions(-)
 create mode 100644 drivers/tty/tty_enum.c

diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index 29dfc24..c0c8dd5 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig
@@ -162,6 +162,9 @@ config LEGACY_PTY_COUNT
 	  When not in use, each legacy PTY occupies 12 bytes on 32-bit
 	  architectures and 24 bytes on 64-bit architectures.
 
+config TTY_ENUM
+	bool
+
 config BFIN_JTAG_COMM
 	tristate "Blackfin JTAG Communication"
 	depends on BLACKFIN
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile
index 35649d3..ce7b340 100644
--- a/drivers/tty/Makefile
+++ b/drivers/tty/Makefile
@@ -1,5 +1,6 @@
 obj-$(CONFIG_TTY)		+= tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o \
 				   tty_buffer.o tty_port.o tty_mutex.o
+obj-$(CONFIG_TTY_ENUM)		+= tty_enum.o
 obj-$(CONFIG_LEGACY_PTYS)	+= pty.o
 obj-$(CONFIG_UNIX98_PTYS)	+= pty.o
 obj-$(CONFIG_AUDIT)		+= tty_audit.o
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index e9aeccd..b269dab 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -764,6 +764,7 @@ config SERIAL_HS_LPC32XX_CONSOLE
 
 config SERIAL_CORE
 	tristate
+	select TTY_ENUM
 
 config SERIAL_CORE_CONSOLE
 	bool
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index ca98a3f..94ae5e1 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -2669,7 +2669,7 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
 	/*
 	 * Remove the devices from the tty layer
 	 */
-	tty_unregister_device(drv->tty_driver, uport->line);
+	tty_port_unregister_device(port, drv->tty_driver, uport->line);
 
 	if (port->tty)
 		tty_vhangup(port->tty);
diff --git a/drivers/tty/tty_enum.c b/drivers/tty/tty_enum.c
new file mode 100644
index 0000000..7947f47
--- /dev/null
+++ b/drivers/tty/tty_enum.c
@@ -0,0 +1,356 @@
+
+/*
+ * tty_enum.c - TTY enumeration support
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+
+
+static LIST_HEAD(tty_bus_id_list);
+static DEFINE_MUTEX(tty_bus_lock);
+
+struct tty_bus_id {
+	char bus_id[TTY_NAME_SIZE];
+	unsigned int instance_no;
+	struct list_head node;
+};
+
+static ssize_t tty_slave_show_name(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct tty_slave *tts = to_tty_slave(dev);
+	return sprintf(buf, "%s\n", tts->name);
+}
+
+static int create_modalias(struct tty_slave *tts, char *modalias, int size)
+{
+	int len;
+	int count;
+	int i;
+
+	if (!tts->nr_ids)
+		return 0;
+
+	len = snprintf(modalias, size, "%s:", TTY_MODULE_PREFIX);
+	size -= len;
+
+	for (i = 0; i < tts->nr_ids; i++) {
+		count = snprintf(&modalias[len], size, "%s:", tts->ids[i]);
+		if (count < 0 || count >= size)
+			return -EINVAL;
+		len += count;
+		size -= count;
+	}
+
+	modalias[len] = '\0';
+	return len;
+}
+
+static ssize_t tty_slave_show_modalias(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct tty_slave *tts = to_tty_slave(dev);
+	size_t len;
+
+	/* Device has no IDs or string is >1024 */
+	len = create_modalias(tts, buf, 1024);
+	if (len <= 0)
+		return 0;
+	buf[len++] = '\n';
+	return len;
+}
+
+static ssize_t tty_slave_show_tty_attrs(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct tty_slave *tts = to_tty_slave(dev);
+	int len = 0;
+
+	/* baud rate */
+	len += sprintf(buf+len, "%d ", tts->baud);
+
+	/* data bits */
+	switch (tts->cflag & CSIZE) {
+	case CS5:
+		len += sprintf(buf+len, "5");
+		break;
+	case CS6:
+		len += sprintf(buf+len, "6");
+		break;
+	case CS7:
+		len += sprintf(buf+len, "7");
+		break;
+	case CS8:
+	default:
+		len += sprintf(buf+len, "8");
+		break;
+	}
+
+	/* parity */
+	if (tts->cflag & PARODD)
+		len += sprintf(buf+len, "O");
+	else if (tts->cflag & PARENB)
+		len += sprintf(buf+len, "E");
+	else
+		len += sprintf(buf+len, "N");
+
+	/* stop bits */
+	len += sprintf(buf+len, "%d", tts->cflag & CSTOPB ? 1 : 0);
+
+	/* HW/SW control */
+	if (tts->cflag & CRTSCTS)
+		len += sprintf(buf+len, " HW");
+	if ((tts->iflag & (IXON|IXOFF|IXANY)) != 0)
+		len += sprintf(buf+len, " SW");
+
+	len += sprintf(buf+len, "\n");
+	return len;
+}
+
+static ssize_t tty_slave_show_modem_lines(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct tty_slave *tts = to_tty_slave(dev);
+	int len = 0;
+
+	/* endian */
+	if (tts->mctrl & TIOCM_LE)
+		len += sprintf(buf+len, "LE:");
+	else
+		len += sprintf(buf+len, "BE:");
+
+	/* terminal lines */
+	if (tts->mctrl & TIOCM_DTR)
+		len += sprintf(buf+len, "DTR,");
+	if (tts->mctrl & TIOCM_RTS)
+		len += sprintf(buf+len, "RTS,");
+
+	/* modem lines */
+	if (tts->mctrl & TIOCM_CTS)
+		len += sprintf(buf+len, "CTS,");
+	if (tts->mctrl & TIOCM_CAR)
+		len += sprintf(buf+len, "CAR,");
+	if (tts->mctrl & TIOCM_RNG)
+		len += sprintf(buf+len, "RNG,");
+	if (tts->mctrl & TIOCM_DSR)
+		len += sprintf(buf+len, "DSR,");
+
+	len += sprintf(buf+len, "\n");
+	return len;
+}
+
+static DEVICE_ATTR(name, S_IRUGO, tty_slave_show_name, NULL);
+static DEVICE_ATTR(modalias, S_IRUGO, tty_slave_show_modalias, NULL);
+static DEVICE_ATTR(tty_attrs, S_IRUGO, tty_slave_show_tty_attrs, NULL);
+static DEVICE_ATTR(modem_lines, S_IRUGO, tty_slave_show_modem_lines, NULL);
+
+static struct attribute *tty_slave_attrs[] = {
+	&dev_attr_name.attr,
+	/* coldplug: modprobe $(cat .../modalias) */
+	&dev_attr_modalias.attr,
+	&dev_attr_tty_attrs.attr,
+	&dev_attr_modem_lines.attr,
+	NULL,
+};
+
+static struct attribute_group tty_slave_group = {
+	.attrs	= tty_slave_attrs,
+};
+
+static const struct attribute_group *tty_slave_groups[] = {
+	&tty_slave_group,
+	NULL,
+};
+
+#ifdef CONFIG_HOTPLUG
+static int tty_slave_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct tty_slave *tts = to_tty_slave(dev);
+	int len;
+
+	if (!tts->nr_ids)
+		return 0;
+
+	if (add_uevent_var(env, "MODALIAS="))
+		return -ENOMEM;
+	len = create_modalias(tts, &env->buf[env->buflen - 1],
+			      sizeof(env->buf) - env->buflen);
+	if (len >= (sizeof(env->buf) - env->buflen))
+		return -ENOMEM;
+	env->buflen += len;
+
+	dev_dbg(dev, "uevent\n");
+	return 0;
+}
+#else
+#define tty_slave_uevent	NULL
+#endif
+
+static void tty_slave_release(struct device *dev)
+{
+	struct tty_slave *tts = to_tty_slave(dev);
+
+	kfree(tts);
+	/* Test code to see if slave device get released */
+	BUG();
+}
+
+struct device_type tty_slave_type = {
+	.name		= "tty_slave",
+	.groups		= tty_slave_groups,
+	.uevent		= tty_slave_uevent,
+	.release	= tty_slave_release,
+};
+EXPORT_SYMBOL_GPL(tty_slave_type);
+
+/**
+ * tty_bus_register_slave - register a physical tty slave device
+ * @port: the tty port
+ * @info: the tty slave device description
+ *
+ * Initialize and add a physical tty slave device.
+ *
+ * This returns the new physical tty slave device, which may be saved for
+ * later use with device_unregister; or NULL to indicate an error.
+ */
+struct tty_slave *tty_bus_register_slave(struct tty_port *port,
+		struct tty_board_info const *info)
+{
+	struct tty_slave *tts;
+	struct tty_bus_id *bus_id, *new_bus_id;
+	struct device *dev = NULL;
+	int i;
+	int status;
+	int found = 0;
+
+	/* Drivers having not called tty_port_register_device, should not
+	 * call this API.
+	 */
+	BUG_ON(!info || !port || !port->dev);
+	dev = port->dev;
+
+	tts = kzalloc(sizeof(struct tty_slave) + info->nr_ids * TTY_NAME_SIZE,
+		      GFP_KERNEL);
+	if (!tts) {
+		dev_err(dev, "Failed to alloc tty_slave %s.\n", info->type);
+		return NULL;
+	}
+
+	new_bus_id = kzalloc(sizeof(struct tty_bus_id), GFP_KERNEL);
+	if (!new_bus_id) {
+		dev_err(dev, "Failed to alloc tty_bus_id for %s.\n",
+			info->type);
+		goto fail;
+	}
+
+	tts->dev.parent = dev;
+	tts->dev.bus = &tty_enum_bus;
+	tts->dev.type = &tty_slave_type;
+
+	tts->dev.platform_data = info->platform_data;
+	if (info->archdata)
+		tts->dev.archdata = *info->archdata;
+
+	tts->baud = info->baud;
+	tts->cflag = info->cflag;
+	tts->iflag = info->iflag;
+	tts->mctrl = info->mctrl;
+	tts->irq = info->irq;
+
+	strlcpy(tts->name, info->type, TTY_NAME_SIZE);
+	for (i = 0; i < info->nr_ids; i++)
+		strlcpy(tts->ids[i], info->ids[i], TTY_NAME_SIZE);
+	tts->nr_ids = info->nr_ids;
+
+	mutex_lock(&tty_bus_lock);
+	list_for_each_entry(bus_id, &tty_bus_id_list, node) {
+		if (!strcmp(bus_id->bus_id, info->type)) {
+			bus_id->instance_no++;
+			found = 1;
+			kfree(new_bus_id);
+			break;
+		}
+	}
+	if (!found) {
+		bus_id = new_bus_id;
+		strcpy(bus_id->bus_id, info->type);
+		bus_id->instance_no = 0;
+		list_add_tail(&bus_id->node, &tty_bus_id_list);
+	}
+	dev_set_name(&tts->dev, "%s:%02x", bus_id->bus_id, bus_id->instance_no);
+	mutex_unlock(&tty_bus_lock);
+
+	status = device_register(&tts->dev);
+	if (status) {
+		dev_err(dev, "Failed to register slave device %s.\n",
+			info->type);
+		goto fail;
+	}
+	dev_info(dev, "Registered slave device %s.\n", info->type);
+
+	return tts;
+
+fail:
+	kfree(tts);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(tty_bus_register_slave);
+
+static int __unregister(struct device *dev, void *null)
+{
+	if (is_tty_slave(dev))
+		device_unregister(dev);
+	return 0;
+}
+
+void tty_bus_unregister_slaves(struct tty_port *port)
+{
+	if (port->dev)
+		(void)device_for_each_child(port->dev, NULL, __unregister);
+}
+EXPORT_SYMBOL_GPL(tty_bus_unregister_slaves);
+
+void tty_bus_register_master(struct tty_port *port, struct device *dev)
+{
+	BUG_ON(!port);
+	if (dev)
+		port->dev = get_device(dev);
+}
+EXPORT_SYMBOL_GPL(tty_bus_register_master);
+
+void tty_bus_unregister_master(struct tty_port *port)
+{
+	BUG_ON(!port);
+	if (port->dev) {
+		tty_bus_unregister_slaves(port);
+		put_device(port->dev);
+		port->dev = NULL;
+	}
+}
+EXPORT_SYMBOL_GPL(tty_bus_unregister_master);
+
+struct bus_type tty_enum_bus = {
+	.name		= "tty_enum",
+};
+EXPORT_SYMBOL_GPL(tty_enum_bus);
+
+static int __init tty_bus_init(void)
+{
+	return bus_register(&tty_enum_bus);
+}
+
+postcore_initcall(tty_bus_init);
diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c
index b7ff59d..8864124 100644
--- a/drivers/tty/tty_port.c
+++ b/drivers/tty/tty_port.c
@@ -69,8 +69,13 @@ struct device *tty_port_register_device(struct tty_port *port,
 		struct tty_driver *driver, unsigned index,
 		struct device *device)
 {
+	struct device *dev;
+
 	tty_port_link_device(port, driver, index);
-	return tty_register_device(driver, index, device);
+	dev = tty_register_device(driver, index, device);
+	tty_bus_register_master(port, dev);
+
+	return dev;
 }
 EXPORT_SYMBOL_GPL(tty_port_register_device);
 
@@ -92,12 +97,33 @@ struct device *tty_port_register_device_attr(struct tty_port *port,
 		struct device *device, void *drvdata,
 		const struct attribute_group **attr_grp)
 {
+	struct device *dev;
+
 	tty_port_link_device(port, driver, index);
-	return tty_register_device_attr(driver, index, device, drvdata,
-			attr_grp);
+	dev = tty_register_device_attr(driver, index, device, drvdata,
+				       attr_grp);
+	tty_bus_register_master(port, dev);
+
+	return dev;
 }
 EXPORT_SYMBOL_GPL(tty_port_register_device_attr);
 
+/**
+ * tty_port_unregister_device_attr - unregister tty device
+ * @port: tty_port of the device
+ * @driver: tty_driver for this device
+ * @index: index of the tty
+ *
+ * The reverse call for tty_register_device.
+ */
+void tty_port_unregister_device(struct tty_port *port,
+		struct tty_driver *driver, unsigned index)
+{
+	tty_bus_unregister_master(port);
+	tty_unregister_device(driver, index);
+}
+EXPORT_SYMBOL_GPL(tty_port_unregister_device);
+
 int tty_port_alloc_xmit_buf(struct tty_port *port)
 {
 	/* We may sleep in get_zeroed_page() */
@@ -132,6 +158,16 @@ EXPORT_SYMBOL(tty_port_free_xmit_buf);
  */
 void tty_port_destroy(struct tty_port *port)
 {
+	/*
+	 * XXX: TTY Driver Cleanup
+	 *
+	 * If you got panic here, it means you have enabled the TTY bus
+	 * enumeration support for your TTY driver but you haven't updated
+	 * your TTY driver code to call the tty_port_unregister_device
+	 * instead of the tty_unregister_device as a destruction
+	 * corresponding to the tty_port_register_device.
+	 */
+	BUG_ON(port->dev);
 	tty_buffer_free_all(port);
 }
 EXPORT_SYMBOL(tty_port_destroy);
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index fed3def..f1c4875 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -433,6 +433,12 @@ struct rpmsg_device_id {
 	char name[RPMSG_NAME_SIZE];
 };
 
+/* tty */
+
+/* TTY slave name size */
+#define TTY_NAME_SIZE	32
+#define TTY_MODULE_PREFIX "tty:"
+
 /* i2c */
 
 #define I2C_NAME_SIZE	20
diff --git a/include/linux/tty.h b/include/linux/tty.h
index c75d886..b8d5dfb 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -9,6 +9,7 @@
 #include <linux/tty_ldisc.h>
 #include <linux/mutex.h>
 #include <linux/tty_flags.h>
+#include <linux/mod_devicetable.h>
 #include <uapi/linux/tty.h>
 
 
@@ -154,6 +155,105 @@ struct tty_bufhead {
 
 struct device;
 struct signal_struct;
+struct tty_slave;
+struct tty_board_info;
+
+/**
+ * struct tty_board_info - template for slave device creation
+ * @type: chip type, to initialize tty_slave.name
+ * @cflag: termio cflag, preferred termio cflag to be used to communicate
+ *         with this slave device
+ * @iflag: termio iflag, preferred termio iflag to be used to communicate
+ *         with this slave device
+ * @mctrl: termio mctrl, preferred termio mctrl to be used to communicate
+ *         with this slave device
+ * @baud: termio baud, preferred termio baud rate to be used to communicate
+ *        with this slave device
+ * @irq: stored in tty_slave.irq
+ * @platform_data: stored in tty_slave.dev.platform_data
+ * @archdata: copied into tty_slave.dev.archdata
+ * @nr_ids: number of IDs
+ * @ids: ID strings
+ *
+ * tty_board_info is used to build tables of information listing TTY
+ * devices that are present. This information is used to grow the driver
+ * model tree. For add-on boards, tty_bus_register_slave() does this
+ * dynamically with the host side physical device already known.
+ */
+struct tty_board_info {
+	char type[TTY_NAME_SIZE];
+	unsigned int cflag;	/* termio cflag */
+	unsigned int iflag;	/* termio iflag */
+	unsigned int mctrl;	/* modem ctrl settings */
+	unsigned int baud;
+	int irq;
+	void *platform_data;
+	struct dev_archdata *archdata;
+
+	int nr_ids;
+	/* This must be the last member of tty_board_info */
+	char ids[0][TTY_NAME_SIZE];
+};
+
+/**
+ * struct tty_slave - represent a TTY slave device
+ * @name: Indicates the type of the device, usually a chip name that's
+ *        generic enough to hide second-sourcing and compatible revisions
+ * @cflag: preferred termio cflag used to communicate with this slave
+ *         device
+ * @iflag: preferred termio iflag used to communicate with this slave
+ *         device
+ * @mctrl: preferred termio mctrl used to communicate with this slave
+ *         device
+ * @baud: preferred termio baud rate used to communicate with this slave
+ *        device
+ * @irq: indicates the IRQ generated by this slave device (if any)
+ * @dev: driver model device node for the slave device
+ * @nr_ids: number of IDs
+ * @ids: ID strings
+ *
+ * A tty_slave identifies a single device (i.e. chip) connected to a tty
+ * port.
+ */
+struct tty_slave {
+	char			name[TTY_NAME_SIZE];
+	unsigned int		cflag;	/* termio cflag */
+	unsigned int		iflag;	/* termio iflag */
+	unsigned int		mctrl;	/* modem ctrl settings */
+	unsigned int		baud;
+	int			irq;	/* irq issued by device */
+	struct device		dev;
+	int			nr_ids;
+	char			ids[0][TTY_NAME_SIZE];
+};
+
+extern struct device_type tty_slave_type;
+
+#define is_tty_slave(d) ((d) && (d)->type == &tty_slave_type)
+#define to_tty_slave(d) container_of(d, struct tty_slave, dev)
+
+static inline struct tty_slave *tty_verify_slave(struct device *dev)
+{
+	return is_tty_slave(dev) ? to_tty_slave(dev) : NULL;
+}
+
+#ifdef CONFIG_TTY_ENUM
+struct tty_slave *tty_bus_register_slave(struct tty_port *port,
+		struct tty_board_info const *info);
+void tty_bus_unregister_slaves(struct tty_port *port);
+void tty_bus_register_master(struct tty_port *port, struct device *dev);
+void tty_bus_unregister_master(struct tty_port *port);
+#else
+static inline struct tty_slave *tty_bus_register_slave(struct tty_port *port,
+		struct tty_board_info const *info)
+{
+	return NULL;
+}
+static inline void tty_bus_unregister_slaves(struct tty_port *port) {}
+static inline void tty_bus_register_master(struct tty_port *port,
+		struct device *dev) {}
+static inline void tty_bus_unregister_master(struct tty_port *port) {}
+#endif
 
 /*
  * Port level information. Each device keeps its own port level information
@@ -189,6 +289,7 @@ struct tty_port_operations {
 	
 struct tty_port {
 	struct tty_bufhead	buf;		/* Locked internally */
+	struct device		*dev;		/* Registered tty device */
 	struct tty_struct	*tty;		/* Back pointer */
 	struct tty_struct	*itty;		/* internal back ptr */
 	const struct tty_port_operations *ops;	/* Port operations */
@@ -358,6 +459,7 @@ extern struct ktermios tty_std_termios;
 extern int vcs_init(void);
 
 extern struct class *tty_class;
+extern struct bus_type tty_enum_bus;
 
 /**
  *	tty_kref_get		-	get a tty reference
@@ -478,6 +580,8 @@ extern struct device *tty_port_register_device_attr(struct tty_port *port,
 		struct tty_driver *driver, unsigned index,
 		struct device *device, void *drvdata,
 		const struct attribute_group **attr_grp);
+extern void tty_port_unregister_device(struct tty_port *port,
+		struct tty_driver *driver, unsigned index);
 extern int tty_port_alloc_xmit_buf(struct tty_port *port);
 extern void tty_port_free_xmit_buf(struct tty_port *port);
 extern void tty_port_destroy(struct tty_port *port);
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v5 2/2] ACPI / UART: Add ACPI enumeration support for UART
  2013-01-24 10:30 ` [PATCH v5 0/2] ACPI/UART: Add ACPI 5.0 enumeration " Lv Zheng
  2013-01-24 10:30   ` [PATCH v5 1/2] TTY: Add TTY slave enumeration support Lv Zheng
@ 2013-01-24 10:30   ` Lv Zheng
  1 sibling, 0 replies; 71+ messages in thread
From: Lv Zheng @ 2013-01-24 10:30 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Alan Cox, Rafael J Wysocki, Len Brown,
	Mika Westerberg
  Cc: linux-acpi, linux-serial, Lv Zheng

ACPI 5.0 specification introduces mechanisms of enumerating the slave
devices connected on the serial buses.
This patch follows the specification, implementing such UART enumeration
machanism for Linux.
In order to use this UART device enumeration mechanism, driver writers
are required to call the following API:
Call acpi_tty_register_devices _after_ the creation of the tty ports:
   tty_port_register_device(port, driver, i, parent);
   acpi_uart_register_devices(port);
Where:
   port: the registered tty port.
   parent: the physical device of the UART ports.
   driver and index: required parameters for tty_port_register_device.
In this patch, only SERIAL_CORE drivers are enabled to use this ACPI
UART enumeration mechanism. This can be changed if there are needs
updated in the future.

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/acpi/Kconfig     |    6 ++
 drivers/acpi/Makefile    |    1 +
 drivers/acpi/acpi_uart.c |  215 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/tty/tty_enum.c   |    1 +
 include/linux/tty.h      |    8 ++
 5 files changed, 231 insertions(+)
 create mode 100644 drivers/acpi/acpi_uart.c

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 38c5078..98e2d4e 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -187,6 +187,12 @@ config ACPI_I2C
 	help
 	  ACPI I2C enumeration support.
 
+config ACPI_UART
+	def_tristate TTY_ENUM
+	depends on TTY_ENUM
+	help
+	  ACPI UART enumeration support.
+
 config ACPI_PROCESSOR
 	tristate "Processor"
 	select THERMAL
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 2a4502b..784f332 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -71,6 +71,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS)	+= ec_sys.o
 obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
 obj-$(CONFIG_ACPI_BGRT)		+= bgrt.o
 obj-$(CONFIG_ACPI_I2C)		+= acpi_i2c.o
+obj-$(CONFIG_ACPI_UART)		+= acpi_uart.o
 
 # processor has its own "processor." module_param namespace
 processor-y			:= processor_driver.o processor_throttling.o
diff --git a/drivers/acpi/acpi_uart.c b/drivers/acpi/acpi_uart.c
new file mode 100644
index 0000000..6b1842a
--- /dev/null
+++ b/drivers/acpi/acpi_uart.c
@@ -0,0 +1,215 @@
+/*
+ * acpi_uart.c - ACPI UART enumeration support
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ */
+
+#include <linux/init.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <acpi/acpi.h>
+#include <acpi/acpi_bus.h>
+
+
+static int acpi_uart_add_resources(struct acpi_resource *ares, void *context)
+{
+	struct tty_board_info *info = context;
+
+	if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
+		struct acpi_resource_uart_serialbus *sb;
+
+		sb = &ares->data.uart_serial_bus;
+		if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_UART)
+			return 1;
+
+		/* baud rate */
+		info->baud = sb->default_baud_rate;
+
+		/* data bits */
+		info->cflag &= ~CSIZE;
+		switch (sb->data_bits) {
+		case ACPI_UART_5_DATA_BITS:
+			info->cflag |= CS5;
+			break;
+		case ACPI_UART_6_DATA_BITS:
+			info->cflag |= CS6;
+			break;
+		case ACPI_UART_7_DATA_BITS:
+			info->cflag |= CS7;
+			break;
+		case ACPI_UART_8_DATA_BITS:
+		default:
+			info->cflag |= CS8;
+			break;
+		}
+
+		/* parity */
+		info->cflag &= ~(PARENB | PARODD);
+		if (sb->parity == ACPI_UART_PARITY_EVEN)
+			info->cflag |= PARENB;
+		else if (sb->parity == ACPI_UART_PARITY_ODD)
+			info->cflag |= (PARENB | PARODD);
+
+		/* stop bits */
+		if (sb->stop_bits == ACPI_UART_2_STOP_BITS)
+			info->cflag |= CSTOPB;
+		else
+			info->cflag &= ~CSTOPB;
+
+		/* HW control */
+		if (sb->flow_control & ACPI_UART_FLOW_CONTROL_HW)
+			info->cflag |= CRTSCTS;
+		else
+			info->cflag &= ~CRTSCTS;
+
+		/* SW control */
+		if (sb->flow_control & ACPI_UART_FLOW_CONTROL_XON_XOFF)
+			info->iflag |= (IXON | IXOFF);
+		else
+			info->iflag &= ~(IXON|IXOFF|IXANY);
+
+		/* endianess */
+		if (sb->endian == ACPI_UART_LITTLE_ENDIAN)
+			info->mctrl |= TIOCM_LE;
+		else
+			info->mctrl &= ~TIOCM_LE;
+
+		/* terminal lines */
+		if (sb->lines_enabled & ACPI_UART_DATA_TERMINAL_READY)
+			info->mctrl |= TIOCM_DTR;
+		else
+			info->mctrl &= ~TIOCM_DTR;
+		if (sb->lines_enabled & ACPI_UART_REQUEST_TO_SEND)
+			info->mctrl |= TIOCM_RTS;
+		else
+			info->mctrl &= ~TIOCM_RTS;
+
+		/* modem lines */
+		if (sb->lines_enabled & ACPI_UART_CLEAR_TO_SEND)
+			info->mctrl |= TIOCM_CTS;
+		else
+			info->mctrl &= ~TIOCM_CTS;
+		if (sb->lines_enabled & ACPI_UART_CARRIER_DETECT)
+			info->mctrl |= TIOCM_CAR;
+		else
+			info->mctrl &= ~TIOCM_CAR;
+		if (sb->lines_enabled & ACPI_UART_RING_INDICATOR)
+			info->mctrl |= TIOCM_RNG;
+		else
+			info->mctrl &= ~TIOCM_RNG;
+		if (sb->lines_enabled & ACPI_UART_DATA_SET_READY)
+			info->mctrl |= TIOCM_DSR;
+		else
+			info->mctrl &= ~TIOCM_DSR;
+	} else if (info->irq < 0) {
+		struct resource r;
+
+		if (acpi_dev_resource_interrupt(ares, 0, &r))
+			info->irq = r.start;
+	}
+
+	return 1;
+}
+
+static acpi_status acpi_uart_add_device(acpi_handle handle, u32 level,
+					void *context, void **return_value)
+{
+	struct tty_port *port = context;
+	struct tty_board_info *board_info;
+	struct acpi_device *adev;
+	struct list_head resource_list;
+	int ret;
+	struct tty_slave *tts;
+	int nr_ids, i;
+	size_t info_size;
+	struct acpi_hardware_id *id;
+	char node_name[5];
+	struct acpi_buffer buffer = { sizeof(node_name), node_name };
+
+	if (acpi_bus_get_device(handle, &adev))
+		return AE_OK;
+	if (acpi_bus_get_status(adev) || !adev->status.present)
+		return AE_OK;
+
+	/* Allocate board info structure. */
+	nr_ids = 0;
+	list_for_each_entry(id, &adev->pnp.ids, list)
+		nr_ids++;
+	info_size = sizeof(struct tty_board_info) + (TTY_NAME_SIZE * nr_ids);
+	board_info = kzalloc(info_size, GFP_KERNEL);
+	if (!board_info)
+		return AE_OK;
+
+	/* Enumerate resources. */
+	board_info->acpi_node.handle = handle;
+	board_info->irq = -1;
+	INIT_LIST_HEAD(&resource_list);
+	ret = acpi_dev_get_resources(adev, &resource_list,
+				     acpi_uart_add_resources, board_info);
+	acpi_dev_free_resource_list(&resource_list);
+	if (ret < 0 || !board_info->baud)
+		goto fail;
+
+	/* Use ACPI node name as device name. */
+	acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer);
+	strlcpy(board_info->type, node_name, TTY_NAME_SIZE);
+
+	/* Store PnP IDs. */
+	i = 0;
+	list_for_each_entry(id, &adev->pnp.ids, list) {
+		if (i >= nr_ids)
+			break;
+		strlcpy(board_info->ids[i], id->id, TTY_NAME_SIZE);
+		i++;
+	}
+	board_info->nr_ids = nr_ids;
+
+	tts = tty_bus_register_slave(port, board_info);
+	if (!tts)
+		dev_err(&adev->dev, "failed to add %s UART device from ACPI\n",
+			dev_name(&adev->dev));
+
+fail:
+	kfree(board_info);
+	return AE_OK;
+}
+
+static struct device *acpi_uart_port_parent(struct tty_port *port)
+{
+	return port->dev ? port->dev->parent : NULL;
+}
+
+/**
+ * acpi_uart_register_devices - register the uart slave devices behind the
+ *                              tty port
+ * @port: the host tty port
+ *
+ * Enumerate all tty slave devices behind the host device by walking the
+ * ACPI namespace. When a device is found, it will be added to the Linux
+ * device model and bound to the corresponding ACPI handle.
+ */
+void acpi_uart_register_devices(struct tty_port *port)
+{
+	acpi_handle handle;
+	acpi_status status;
+	struct device *dev;
+
+	dev = acpi_uart_port_parent(port);
+	if (!dev)
+		return;
+	handle = ACPI_HANDLE(dev);
+	if (!handle)
+		return;
+
+	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
+				     acpi_uart_add_device, NULL, port, NULL);
+	if (ACPI_FAILURE(status))
+		dev_warn(dev, "failed to enumerate UART slaves\n");
+}
+EXPORT_SYMBOL_GPL(acpi_uart_register_devices);
diff --git a/drivers/tty/tty_enum.c b/drivers/tty/tty_enum.c
index 7947f47..2218629 100644
--- a/drivers/tty/tty_enum.c
+++ b/drivers/tty/tty_enum.c
@@ -260,6 +260,7 @@ struct tty_slave *tty_bus_register_slave(struct tty_port *port,
 	tts->dev.parent = dev;
 	tts->dev.bus = &tty_enum_bus;
 	tts->dev.type = &tty_slave_type;
+	ACPI_HANDLE_SET(&tts->dev, info->acpi_node.handle);
 
 	tts->dev.platform_data = info->platform_data;
 	if (info->archdata)
diff --git a/include/linux/tty.h b/include/linux/tty.h
index b8d5dfb..8b60e47 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -172,6 +172,7 @@ struct tty_board_info;
  * @irq: stored in tty_slave.irq
  * @platform_data: stored in tty_slave.dev.platform_data
  * @archdata: copied into tty_slave.dev.archdata
+ * @acpi_node: ACPI device node
  * @nr_ids: number of IDs
  * @ids: ID strings
  *
@@ -189,6 +190,7 @@ struct tty_board_info {
 	int irq;
 	void *platform_data;
 	struct dev_archdata *archdata;
+	struct acpi_dev_node acpi_node;
 
 	int nr_ids;
 	/* This must be the last member of tty_board_info */
@@ -763,4 +765,10 @@ do {									\
 } while (0)
 
 
+#if IS_ENABLED(CONFIG_ACPI_UART)
+void acpi_uart_register_devices(struct tty_port *port);
+#else
+static inline void acpi_uart_register_devices(struct tty_port *port) {}
+#endif
+
 #endif
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [RFC PATCH v5] HACK: UART: Add dummy devices to test the enumeration
  2012-12-03  3:39 [RFC PATCH 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
                   ` (7 preceding siblings ...)
  2013-01-24 10:30 ` [PATCH v5 0/2] ACPI/UART: Add ACPI 5.0 enumeration " Lv Zheng
@ 2013-01-24 10:30 ` Lv Zheng
  2013-02-06  6:26 ` [RFC PATCH] ACPI / serial: Add UART change_pm support with ACPI power domain Lv Zheng
  2013-04-03  2:05 ` [PATCH v6] ACPI / serial: Add peripheral PnP IDs enumeration support Lv Zheng
  10 siblings, 0 replies; 71+ messages in thread
From: Lv Zheng @ 2013-01-24 10:30 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Alan Cox, Rafael J Wysocki, Len Brown,
	Mika Westerberg
  Cc: linux-acpi, linux-serial, Lv Zheng

This is a test patch that should not be merged into any of the published
Linux source tree.
This test is useful on platforms having not ACPI 5.0 UART serial enumerated
devices equipped.

The test is based on the following customized DSDT (containing the dummy
uart host adapter INTF000 and target device INTF001):

1. customized DSDT:
  Device (UA00)
  {
      Name (_HID, "INTF000")  // _HID: Hardware ID
      Name (RBUF, ResourceTemplate ()
      {
          Memory32Fixed (ReadWrite,
              0x00000000,         // Address Base
              0x00001000)         // Address Length
      })
      Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
      {
          Return (RBUF)
      }
      Method (_STA, 0, NotSerialized)  // _STA: Status
      {
          Return (0x0F)
      }
      Device (BTH0)
      {
          Name (_HID, "INTF001")  // _HID: Hardware ID
          Name (_CID, "PNPF001")  // _CID: Compatible ID
          Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
          {
              Name (UBUF, ResourceTemplate ()
              {
                  UartSerialBus (0x0001C200, DataBitsEight, StopBitsOne,
                      0xC0, LittleEndian, ParityTypeNone, FlowControlHardware,
                      0x0020, 0x0020, "\\_SB.PCI0.UA00",
                      0x00, ResourceConsumer, ,
                      )
              })
              Return (UBUF)
          }
          Method (_STA, 0, NotSerialized)  // _STA: Status
          {
              Return (0x0F)
          }
      }
  }

2. uevent monitor:
# udevadm monitor --kernel --environment > ~/uart.uevents
# echo add > /sys/class/tty/uevent
# echo add > /sys/class/tty/devices/BTH0:00/uevent
# cat ~/uart.uevents
monitor will print the received events for:
KERNEL - the kernel uevent

KERNEL[252.443458] add      /bus/tty_enum (bus)
ACTION=add
DEVPATH=/bus/tty_enum
SEQNUM=1142
SUBSYSTEM=bus

KERNEL[268.491709] add      /devices/platform/INTF000:00/tty/ttyS0/BTH0:00 (tty)
ACTION=add
DEVPATH=/devices/platform/INTF000:00/tty/ttyS0/BTH0:00
DEVTYPE=tty_slave
MODALIAS=tty::INTF001:PNPF001:
SEQNUM=1144
SUBSYSTEM=tty

3. kobjects attributes and links:
# cat /sys/bus/tty_enum/devices/BTH0:00/modalias
tty::INTF001:PNPF001:
# cat /sys/bus/tty_enum/devices/BTH0:00/tty_attrs
115200 8N0 HW
# cat /sys/bus/tty_enum/devices/BTH0:00/modem_lines
LE:RTS,CTS,

# ls -l /sys/bus/tty_enum/devices/
BTH0:00 -> ../../../devices/platform/INTF000:00/tty/ttyS0/tty/BTH0:00
# ls -l /sys/devices/platform/INTF000:00/tty/ttyS0/BTH0:00/
firmware_node -> ../../../../../LNXSYSTM:00/device:00/INTF000:00/INTF001:00
subsystem -> ../../../../../../bus/tty_enum
# ls -l /sys/bus/acpi/devices/INTF001:00/
physical_node -> ../../../../pnp0/00:00
physical_node1 -> ../../../../platform/INTF000:00/tty/ttyS0/BTH0:00

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/acpi/scan.c                  |    1 +
 drivers/tty/serial/8250/8250.c       |    5 +-
 drivers/tty/serial/8250/8250_dummy.c |  103 ++++++++++++++++++++++++++++++++++
 drivers/tty/serial/8250/Kconfig      |   10 ++++
 drivers/tty/serial/8250/Makefile     |    1 +
 include/linux/serial_8250.h          |    4 ++
 include/linux/serial_core.h          |   12 ++++
 7 files changed, 133 insertions(+), 3 deletions(-)
 create mode 100644 drivers/tty/serial/8250/8250_dummy.c

diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index c88be6c..3a0f259 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -36,6 +36,7 @@ static const char *dummy_hid = "device";
 static const struct acpi_device_id acpi_platform_device_ids[] = {
 
 	{ "PNP0D40" },
+	{ "INTF000" },
 
 	/* Haswell LPSS devices */
 	{ "INT33C0", 0 },
diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c
index cb7db5a..663e077 100644
--- a/drivers/tty/serial/8250/8250.c
+++ b/drivers/tty/serial/8250/8250.c
@@ -56,8 +56,6 @@ static unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
 
 static unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS;
 
-static struct uart_driver serial8250_reg;
-
 static int serial_index(struct uart_port *port)
 {
 	return (serial8250_reg.minor - 64) + port->line;
@@ -2938,7 +2936,7 @@ int serial8250_find_port(struct uart_port *p)
 #define SERIAL8250_CONSOLE	NULL
 #endif
 
-static struct uart_driver serial8250_reg = {
+struct uart_driver serial8250_reg = {
 	.owner			= THIS_MODULE,
 	.driver_name		= "serial",
 	.dev_name		= "ttyS",
@@ -2946,6 +2944,7 @@ static struct uart_driver serial8250_reg = {
 	.minor			= 64,
 	.cons			= SERIAL8250_CONSOLE,
 };
+EXPORT_SYMBOL_GPL(serial8250_reg);
 
 /*
  * early_serial_setup - early registration for 8250 ports
diff --git a/drivers/tty/serial/8250/8250_dummy.c b/drivers/tty/serial/8250/8250_dummy.c
new file mode 100644
index 0000000..e5aadeb
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_dummy.c
@@ -0,0 +1,103 @@
+/*
+ * 8250_dummy.c: Dummy 8250 UART target device enumerator
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/serial_8250.h>
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct dummy8250_data {
+	int	last_lcr;
+	int	line;
+};
+
+static void dummy8250_serial_out(struct uart_port *p, int offset, int value)
+{
+}
+
+static unsigned int dummy8250_serial_in(struct uart_port *p, int offset)
+{
+	return 0;
+}
+
+static int __devinit dummy8250_probe(struct platform_device *pdev)
+{
+	struct uart_8250_port uart = {};
+	struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	struct dummy8250_data *data;
+
+	if (!regs) {
+		dev_err(&pdev->dev, "no registers defined\n");
+		return -EINVAL;
+	}
+
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+	uart.port.private_data = data;
+
+	spin_lock_init(&uart.port.lock);
+	uart.port.mapbase = regs->start;
+	uart.port.type = PORT_8250;
+	uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP |
+		UPF_FIXED_PORT | UPF_FIXED_TYPE;
+	uart.port.dev = &pdev->dev;
+
+	uart.port.iotype = UPIO_MEM;
+	uart.port.serial_in = dummy8250_serial_in;
+	uart.port.serial_out = dummy8250_serial_out;
+	uart.port.uartclk = 40000000;
+
+	data->line = serial8250_register_8250_port(&uart);
+	if (data->line < 0)
+		return data->line;
+
+#ifdef CONFIG_ACPI_UART
+	acpi_uart_register_devices(serial8250_tty_port(data->line));
+#endif
+	platform_set_drvdata(pdev, data);
+
+	return 0;
+}
+
+static int __devexit dummy8250_remove(struct platform_device *pdev)
+{
+	struct dummy8250_data *data = platform_get_drvdata(pdev);
+
+	serial8250_unregister_port(data->line);
+	return 0;
+}
+
+static const struct acpi_device_id dummy8250_match[] = {
+	{ .id = "INTF000" },
+	{ /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(acpi, dummy8250_match);
+
+static struct platform_driver dummy8250_platform_driver = {
+	.driver = {
+		.name			= "dummy-uart",
+		.owner			= THIS_MODULE,
+		.acpi_match_table	= dummy8250_match,
+	},
+	.probe				= dummy8250_probe,
+	.remove				= __devexit_p(dummy8250_remove),
+};
+
+module_platform_driver(dummy8250_platform_driver);
+
+MODULE_AUTHOR("Lv Zheng");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Dummy 8250 serial port driver");
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index d31f4c6..57ca8a1 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -269,6 +269,16 @@ config SERIAL_8250_DW
 	  Selecting this option will enable handling of the extra features
 	  present in the Synopsys DesignWare APB UART.
 
+config SERIAL_8250_DUMMY
+	tristate "Support for dummy ACPI 8250"
+	depends on SERIAL_8250 && ACPI
+	help
+	  Selecting this option will enable a test UART target device DUMMY
+	  under the ISA serial8250 and a test UART host adapter INTF000
+	  as an platform device for the purpose of testing the ACPI UART
+	  enumeration support.
+	  If unsure, say "N" here.
+
 config SERIAL_8250_EM
 	tristate "Support for Emma Mobile integrated serial port"
 	depends on SERIAL_8250 && ARM && HAVE_CLK
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index a23838a..39142aa 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -20,3 +20,4 @@ obj-$(CONFIG_SERIAL_8250_HUB6)		+= 8250_hub6.o
 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_DUMMY)		+= 8250_dummy.o
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index af47a8a..b61aa6c 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -100,6 +100,10 @@ struct uart_8250_port {
 	void			(*dl_write)(struct uart_8250_port *, int);
 };
 
+extern struct uart_driver serial8250_reg;
+
+#define serial8250_tty_port(line)	uart_tty_port(&serial8250_reg, (line))
+
 int serial8250_register_8250_port(struct uart_8250_port *);
 void serial8250_unregister_port(int line);
 void serial8250_suspend_port(int line);
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 82aebc8..e620fc1 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -257,6 +257,18 @@ struct uart_driver {
 	struct tty_driver	*tty_driver;
 };
 
+static inline struct tty_port *uart_tty_port(struct uart_driver *drv,
+					     unsigned int line)
+{
+	struct uart_state *state;
+
+	if (line >= drv->nr)
+		return NULL;
+
+	state = drv->state + line;
+	return &state->port;
+}
+
 void uart_write_wakeup(struct uart_port *port);
 
 /*
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* Re: [PATCH v5 1/2] TTY: Add TTY slave enumeration support
  2013-01-24 10:30   ` [PATCH v5 1/2] TTY: Add TTY slave enumeration support Lv Zheng
@ 2013-01-25 21:41     ` Greg Kroah-Hartman
  2013-01-26  0:32       ` Alan Cox
  2013-01-26  3:04       ` Zheng, Lv
  2013-01-25 21:45     ` Greg Kroah-Hartman
  1 sibling, 2 replies; 71+ messages in thread
From: Greg Kroah-Hartman @ 2013-01-25 21:41 UTC (permalink / raw)
  To: Lv Zheng
  Cc: Alan Cox, Rafael J Wysocki, Len Brown, Mika Westerberg,
	linux-acpi, linux-serial

On Thu, Jan 24, 2013 at 06:30:21PM +0800, Lv Zheng wrote:
> In the recent ACPI 5.0 specification updates, firmwares are provided the
> possibilities to enumerate the UART slave devices known to the platform
> vendors.
> There are the needs in Linux to utilize the benefits:
> 1. hotplug uevent
> 2. serial configuration
> Currently, only serial cards on the specific bus (ex. PCMCIA) can be
> enumerated and userspace can obtain the hotplug event of the UART target
> devices. Linux kernel is lack of an overall enumeration mechanism for
> UART slave devices.

Huh?  That's flat out not true, otherwise how would userspace be
creating the proper /dev/tty* nodes for all tty devices?

> In order to send uevent, a device need to be a class device or a bus
> device. This patch introduces a tty_enum bus since the enumerated slave
> devices are expected to be physical devices.

Again, tty devices are already class devices, and they send out uevents.
You can see this today by watching the uevent stream using a tool like
'udevadmin monitor'.

> When the UART slave devices are created, userspace uevent rules can
> pass the creation details to the userspace driver managers
> (ex. hciattach), then the device managers can read hardware IDs and the
> serial configurations from the exported device attributes to match and
> configure a userspace TTY device driver.

What?  We do that today, no kernel changes needed.

> The created slave devices will be automatically unregistered when the
> associated TTY ports are destructed.

Again, this happens today.

Or am I missing something big and major here?  How have people been
seeing and configuring their tty devices for the last 8+ years or so
since the 2.6.0 kernel was released with uevent support for tty devices?

lost,

greg k-h

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v5 1/2] TTY: Add TTY slave enumeration support
  2013-01-24 10:30   ` [PATCH v5 1/2] TTY: Add TTY slave enumeration support Lv Zheng
  2013-01-25 21:41     ` Greg Kroah-Hartman
@ 2013-01-25 21:45     ` Greg Kroah-Hartman
  2013-01-26  3:12       ` Zheng, Lv
  1 sibling, 1 reply; 71+ messages in thread
From: Greg Kroah-Hartman @ 2013-01-25 21:45 UTC (permalink / raw)
  To: Lv Zheng
  Cc: Alan Cox, Rafael J Wysocki, Len Brown, Mika Westerberg,
	linux-acpi, linux-serial

On Thu, Jan 24, 2013 at 06:30:21PM +0800, Lv Zheng wrote:
> +static void tty_slave_release(struct device *dev)
> +{
> +	struct tty_slave *tts = to_tty_slave(dev);
> +
> +	kfree(tts);
> +	/* Test code to see if slave device get released */
> +	BUG();
> +}

You send me a patch like this and expect me to apply it when it's
obvious you didn't even test it out?  That's the most _basic_ test for
any dynamic type of device (add it and remove it and make sure the
kernel still works.)  And obviously you didn't do it.

I need a really good reason why to take you seriously now.

Intel kernel developers who know better, please take this internally and
review it a bunch of times, I don't want to see it anytime soon again.

greg k-h

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v5 1/2] TTY: Add TTY slave enumeration support
  2013-01-26  0:32       ` Alan Cox
@ 2013-01-26  0:21         ` Greg Kroah-Hartman
  0 siblings, 0 replies; 71+ messages in thread
From: Greg Kroah-Hartman @ 2013-01-26  0:21 UTC (permalink / raw)
  To: Alan Cox
  Cc: Lv Zheng, Rafael J Wysocki, Len Brown, Mika Westerberg,
	linux-acpi, linux-serial

On Sat, Jan 26, 2013 at 12:32:16AM +0000, Alan Cox wrote:
> > Or am I missing something big and major here?  How have people been
> > seeing and configuring their tty devices for the last 8+ years or so
> > since the 2.6.0 kernel was released with uevent support for tty
> > devices?
> 
> Minor more than major. The newer ACPI tries to begin to describe what
> is on the other end of serial busses being used for internal purposes.
> 
> Now there are several good reasons we want to know what is on the end
> of a port already that are not currently handled, and I have suggested
> a port "type" attribute would be useful (eg  unknown, modem, bluetooth,
> ax25, dcc, etc) so the kernel can help user space enumerate in user
> friendly fashion.

That's fine, just add a single attribute to the existing tty class
device, not a whole new class, which is what this code does.

> Where it gets slightly more tricky is that we are beginning to see
> systems where the firmware is describing
> 
> 	serial port (and power management)
> 	device behind port (and power management)
> 
> so there is a power management hierarchy involved.

Ick, that's going to get messy fast.  There's no hope they would switch
to a USB connection instead for these devices, could they?  That would
fix up most of these issues, and make the devices discoverable.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v5 1/2] TTY: Add TTY slave enumeration support
  2013-01-25 21:41     ` Greg Kroah-Hartman
@ 2013-01-26  0:32       ` Alan Cox
  2013-01-26  0:21         ` Greg Kroah-Hartman
  2013-01-26  3:04       ` Zheng, Lv
  1 sibling, 1 reply; 71+ messages in thread
From: Alan Cox @ 2013-01-26  0:32 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Lv Zheng, Rafael J Wysocki, Len Brown, Mika Westerberg,
	linux-acpi, linux-serial

> Or am I missing something big and major here?  How have people been
> seeing and configuring their tty devices for the last 8+ years or so
> since the 2.6.0 kernel was released with uevent support for tty
> devices?

Minor more than major. The newer ACPI tries to begin to describe what
is on the other end of serial busses being used for internal purposes.

Now there are several good reasons we want to know what is on the end
of a port already that are not currently handled, and I have suggested
a port "type" attribute would be useful (eg  unknown, modem, bluetooth,
ax25, dcc, etc) so the kernel can help user space enumerate in user
friendly fashion.

Where it gets slightly more tricky is that we are beginning to see
systems where the firmware is describing

	serial port (and power management)
	device behind port (and power management)

so there is a power management hierarchy involved.

But yes - these patches should have been labelled 'RFC' - the concept
needs discussion in public not the current code drafts.

Alan

^ permalink raw reply	[flat|nested] 71+ messages in thread

* RE: [PATCH v5 1/2] TTY: Add TTY slave enumeration support
  2013-01-25 21:41     ` Greg Kroah-Hartman
  2013-01-26  0:32       ` Alan Cox
@ 2013-01-26  3:04       ` Zheng, Lv
  2013-01-26  3:42         ` Greg Kroah-Hartman
  1 sibling, 1 reply; 71+ messages in thread
From: Zheng, Lv @ 2013-01-26  3:04 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Alan Cox, Wysocki, Rafael J, Brown, Len, Mika Westerberg,
	linux-acpi, linux-serial, Jiri Slaby (jslaby@suse.cz),
	Heikki Krogerus (heikki.krogerus@linux.intel.com)

Thanks for your suggestions.
All your concerns are against the needs of creating such slave devices.
Let me delete all of the others and reply once.

> > In order to send uevent, a device need to be a class device or a bus
> > device. This patch introduces a tty_enum bus since the enumerated
> > slave devices are expected to be physical devices.
> 
> Again, tty devices are already class devices, and they send out uevents.
> You can see this today by watching the uevent stream using a tool like
> 'udevadmin monitor'.

Please consider the following real scenario when a ttyS0 driver module gets loaded at kernel runtime:
1. the tty controller driver creates ttyS0
2. user gets uevent notified and doesn't know how to use this tty device
3. a slave ACPI node under the ttyS0 gets enumerated and attributes gets appended
4. user need to do user space effort to regenerate the uevent to get a Bluetooth hci user driver matched

If the step 3 can be put ahead of step 2, there might not be weakness without this enhancement.
Considering a more complex scenario:
There are two or more target devices under the ttyS0 and can be selected by some external GPIOs.
Actually, BIOS will do this putting many test devices under the ttyS0.
The step 3 then can never be put ahead of step 2.
(It might be implemented using some exist kernel mechanisms like the SCSI contribute container devices...)

With this patch:
1. a tty controller driver creates ttyS0
2. user gets the uevent notified and need to do nothing
3. a slave device BTH0:00 gets enumerated and created
4. user gets the uevent notified and uses the attributes exported by this device to do a proper Bluetooth hci user driver matching
The latter is more generic which is equivalent to the exist PCMCIA Bluetooth hci matching. And I just think we could get more benefits when we do PM enabling for such uart slave devices.
And I think this patch would not change any currently used user space behavior.
But the user space behavior can be unified if such slave device nodes are there.

Best regards
-Lv


^ permalink raw reply	[flat|nested] 71+ messages in thread

* RE: [PATCH v5 1/2] TTY: Add TTY slave enumeration support
  2013-01-25 21:45     ` Greg Kroah-Hartman
@ 2013-01-26  3:12       ` Zheng, Lv
  2013-01-26  3:44         ` Greg Kroah-Hartman
  0 siblings, 1 reply; 71+ messages in thread
From: Zheng, Lv @ 2013-01-26  3:12 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Alan Cox, Wysocki, Rafael J, Brown, Len, Mika Westerberg,
	linux-acpi, linux-serial, Jiri Slaby (jslaby@suse.cz),
	Heikki Krogerus (heikki.krogerus@linux.intel.com)

> > +static void tty_slave_release(struct device *dev) {
> > +	struct tty_slave *tts = to_tty_slave(dev);
> > +
> > +	kfree(tts);
> > +	/* Test code to see if slave device get released */
> > +	BUG();
> > +}
> 
> You send me a patch like this and expect me to apply it when it's obvious you
> didn't even test it out?  That's the most _basic_ test for any dynamic type of
> device (add it and remove it and make sure the kernel still works.)  And
> obviously you didn't do it.
> 
> I need a really good reason why to take you seriously now.

It's actually my fault.
I'm lack of release testability then.
Now I can do this exactly.
According to internal reviewers' comments, it should be RFC patch to see if any objections there in the community.
I'll complete the test and refresh the patches if you think the functionality is interesting and useful.
Please be noted the device tree guys can also get benefit from such tty target enumeration mechanism.

Thanks
-Lv


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v5 1/2] TTY: Add TTY slave enumeration support
  2013-01-26  3:04       ` Zheng, Lv
@ 2013-01-26  3:42         ` Greg Kroah-Hartman
  2013-01-28  2:58           ` Zheng, Lv
  0 siblings, 1 reply; 71+ messages in thread
From: Greg Kroah-Hartman @ 2013-01-26  3:42 UTC (permalink / raw)
  To: Zheng, Lv
  Cc: Alan Cox, Wysocki, Rafael J, Brown, Len, Mika Westerberg,
	linux-acpi, linux-serial, Jiri Slaby (jslaby@suse.cz),
	Heikki Krogerus (heikki.krogerus@linux.intel.com)

On Sat, Jan 26, 2013 at 03:04:33AM +0000, Zheng, Lv wrote:
> Thanks for your suggestions.
> All your concerns are against the needs of creating such slave devices.
> Let me delete all of the others and reply once.
> 
> > > In order to send uevent, a device need to be a class device or a bus
> > > device. This patch introduces a tty_enum bus since the enumerated
> > > slave devices are expected to be physical devices.
> > 
> > Again, tty devices are already class devices, and they send out uevents.
> > You can see this today by watching the uevent stream using a tool like
> > 'udevadmin monitor'.
> 
> Please consider the following real scenario when a ttyS0 driver module gets loaded at kernel runtime:
> 1. the tty controller driver creates ttyS0

Fine.

> 2. user gets uevent notified and doesn't know how to use this tty device

Of course, that's normal.

> 3. a slave ACPI node under the ttyS0 gets enumerated and attributes gets appended

How will that happen?  Who causes that to happen?  When would that
happen?

> 4. user need to do user space effort to regenerate the uevent to get a Bluetooth hci user driver matched

Whatever caused step 3 to happen can send a new uevent saying something
has changed.  That's a one line patch.

> If the step 3 can be put ahead of step 2, there might not be weakness without this enhancement.

Yes.

> Considering a more complex scenario:
> There are two or more target devices under the ttyS0 and can be selected by some external GPIOs.
> Actually, BIOS will do this putting many test devices under the ttyS0.
> The step 3 then can never be put ahead of step 2.
> (It might be implemented using some exist kernel mechanisms like the SCSI contribute container devices...)

No, don't look at SCSI for sysfs ideas or driver core usage, it's a
mess and is wrong in places.  Look at how USB and PCI do it if you are
curious how to implement a bus.

But again, this isn't a bus, this is a TTY class, and again, we already
have one, so use it, don't create another one to stack on top of it,
that's crazy.

greg k-h

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v5 1/2] TTY: Add TTY slave enumeration support
  2013-01-26  3:12       ` Zheng, Lv
@ 2013-01-26  3:44         ` Greg Kroah-Hartman
  2013-01-28  3:02           ` Zheng, Lv
  0 siblings, 1 reply; 71+ messages in thread
From: Greg Kroah-Hartman @ 2013-01-26  3:44 UTC (permalink / raw)
  To: Zheng, Lv
  Cc: Alan Cox, Wysocki, Rafael J, Brown, Len, Mika Westerberg,
	linux-acpi, linux-serial, Jiri Slaby (jslaby@suse.cz),
	Heikki Krogerus (heikki.krogerus@linux.intel.com)

On Sat, Jan 26, 2013 at 03:12:07AM +0000, Zheng, Lv wrote:
> > > +static void tty_slave_release(struct device *dev) {
> > > +	struct tty_slave *tts = to_tty_slave(dev);
> > > +
> > > +	kfree(tts);
> > > +	/* Test code to see if slave device get released */
> > > +	BUG();
> > > +}
> > 
> > You send me a patch like this and expect me to apply it when it's obvious you
> > didn't even test it out?  That's the most _basic_ test for any dynamic type of
> > device (add it and remove it and make sure the kernel still works.)  And
> > obviously you didn't do it.
> > 
> > I need a really good reason why to take you seriously now.
> 
> It's actually my fault.
> I'm lack of release testability then.
> Now I can do this exactly.
> According to internal reviewers' comments, it should be RFC patch to
> see if any objections there in the community.

Don't put code like this in RFC code either, that shows you didn't even
test it out, and want to waste other people's time.

And note, I almost _never_ read RFC patches, so you aren't going to get
a review from me for it, sorry, I have too many other things to do.

> I'll complete the test and refresh the patches if you think the
> functionality is interesting and useful.  Please be noted the device
> tree guys can also get benefit from such tty target enumeration
> mechanism.

No, they can use what we have today, don't reinvent the wheel.

greg k-h

^ permalink raw reply	[flat|nested] 71+ messages in thread

* RE: [PATCH v5 1/2] TTY: Add TTY slave enumeration support
  2013-01-26  3:42         ` Greg Kroah-Hartman
@ 2013-01-28  2:58           ` Zheng, Lv
  2013-01-30  4:46             ` Greg Kroah-Hartman
  0 siblings, 1 reply; 71+ messages in thread
From: Zheng, Lv @ 2013-01-28  2:58 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Alan Cox, Wysocki, Rafael J, Brown, Len, Mika Westerberg,
	linux-acpi, linux-serial, Jiri Slaby (jslaby@suse.cz),
	Heikki Krogerus (heikki.krogerus@linux.intel.com)

Hi, Greg

Thanks for the comments.
How I wished so much that I can have you opinion earlier because I have some design aspects need to be discussed with the driver core and PM core designers.

> > Considering a more complex scenario:
> > There are two or more target devices under the ttyS0 and can be selected by
> some external GPIOs.
> > Actually, BIOS will do this putting many test devices under the ttyS0.
> > The step 3 then can never be put ahead of step 2.

What about this DSDT, this is what we actually can obtain from some real BIOS.
How can a single ttyS0 attribute file enumerate so many slave devices under single UART:
            Device (UA00)
            {
                Device (BTH0)
                {
                    Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
                    {
                        Name (UBUF, ResourceTemplate ()
                        {
                            UartSerialBus (...)
                        })
                        Return (UBUF)
                    }
                }

                Device (GPS0)
                {
                    Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
                    {
                        Name (UBUF, ResourceTemplate ()
                        {
                            UartSerialBus (...)
                        })
                        Return (UBUF)
                    }
                }

                Device (SER0)
                {
                    Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
                    {
                        Name (UBUF, ResourceTemplate ()
                        {
                            UartSerialBus (...)
                        })
                        Return (UBUF)
                    }
                }

                Device (SER1)
                {
                    Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
                    {
                        Name (UBUF, ResourceTemplate ()
                        {
                            UartSerialBus (...)
                        })
                        Return (UBUF)
                    }
                }
            }

This DSDT is compatible with recent ACPI 5.0 updates on the new functionality - UART serial enumeration.

> > (It might be implemented using some exist kernel mechanisms like the
> > SCSI contribute container devices...)
> 
> No, don't look at SCSI for sysfs ideas or driver core usage, it's a mess and is
> wrong in places.  Look at how USB and PCI do it if you are curious how to
> implement a bus.

Yes, I can see this.
I've completed a driver model investigation prior to authorizing this patch set.
IMO, there are many driver models existed in the kernel:
1. bus driver model: where the subsystem is a bus, IMO, hardware related PM operations should only be enabled on such devices.
2. user driver model: there are devt(s) associated to such devices and the subsystem is a class
3. transport driver model: the SCSI hacks where SCSI happens to be a bus thus ATA has to be a transport class
And so on (system driver model, net driver model)...
IMO, a PM friendly device driver hierarchy should:
1. implement hardware related PM operations on bus devices.
2. other devices should call APIs exported by the functionality of the bus to inc/dec PM refcnts where the bus devices resident.
Thus, tty class device is not suitable for enabling hardware related PM operations that might appear in the slave devices.

If you've monitored this series of patch set, you'll find the v2 patches.
Here is the links:
http://marc.info/?l=linux-serial&m=135467950218063&w=2
http://marc.info/?l=linux-serial&m=135467951018065&w=2
http://marc.info/?l=linux-serial&m=135467951818068&w=2
http://marc.info/?l=linux-serial&m=135467953118073&w=2
http://marc.info/?l=linux-serial&m=135467953218074&w=2

I actually followed the following existing kernel facilities to implement my target: ACPI 5.0 UART enumeration:
1. following the usb-serial-bus, implemented the serial-bus and created target devices below the serial-bus.
2. following the ACPI 5.0 I2C enumeration, implemented the UART enumeration.

According to Alan's comments, implementing tty_enum bus can help to extend this benefit to other serial drivers which do not relies on the serial layer (CONFIG_SERIAL_CORE).

Considering the analogy to the SPI bus layer:
Platform bus device / pci bus device
  |
Spi master device (this is also a class device and can be easily converted to a devt device)
  |
Spi slave device (spi bus device)
The PM call chains will call the APIs exported by the SPI cores which will:
1. inc/dec PM refcnts implemented in the spi bus device drivers for spi slaves and
2. inc/dec PM refcnts implemented in the pdev/pci bus device drivers for spi masters.

This hierarchy looks reasonable if we convert it into a Windows compatible driver model:
Platform bus device / pci bus device (PDOs)
  |
Spi master device (FDOs)
  |
Spi slave device (PDOs)

Thus, I accepted the suggestion, moving the uart target device from uart layer.
This has been done in the v3-v4 revisions.
And I don't see any objections in the community, thus I converted the v4 into v5, removing the RFC marks.
Now the hierarchy will look like:
Platform bus device / pci bus device (PDOs)
  |
Tty communication device (FDOs)
  |
tty slave device (PDOs, the new tty_enum bus device)

Which one do you think is better? V2 or V5 (I have completed the test and now I have the V6 ones, I can send them out if you think the tty_enum bus is better than the serial_bus).

Thanks and best regards
-Lv

^ permalink raw reply	[flat|nested] 71+ messages in thread

* RE: [PATCH v5 1/2] TTY: Add TTY slave enumeration support
  2013-01-26  3:44         ` Greg Kroah-Hartman
@ 2013-01-28  3:02           ` Zheng, Lv
  0 siblings, 0 replies; 71+ messages in thread
From: Zheng, Lv @ 2013-01-28  3:02 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Alan Cox, Wysocki, Rafael J, Brown, Len, Mika Westerberg,
	linux-acpi, linux-serial, Jiri Slaby (jslaby@suse.cz),
	Heikki Krogerus (heikki.krogerus@linux.intel.com)

Hi,

> > It's actually my fault.
> > I'm lack of release testability then.
> > Now I can do this exactly.
> > According to internal reviewers' comments, it should be RFC patch to
> > see if any objections there in the community.
> 
> Don't put code like this in RFC code either, that shows you didn't even test it
> out, and want to waste other people's time.
> 
> And note, I almost _never_ read RFC patches, so you aren't going to get a
> review from me for it, sorry, I have too many other things to do.

It's my great pleasure to have you taking a look at my patches.

> > I'll complete the test and refresh the patches if you think the
> > functionality is interesting and useful.  Please be noted the device
> > tree guys can also get benefit from such tty target enumeration
> > mechanism.
> 
> No, they can use what we have today, don't reinvent the wheel.

According to my previous reply, I'm not trying to reinvent the wheel but trying to fix some gaps in UART slave device enumeration which is introduced in the recent ACPI 5.0 spec updates.

Thanks and best regards
-Lv

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v5 1/2] TTY: Add TTY slave enumeration support
  2013-01-28  2:58           ` Zheng, Lv
@ 2013-01-30  4:46             ` Greg Kroah-Hartman
  0 siblings, 0 replies; 71+ messages in thread
From: Greg Kroah-Hartman @ 2013-01-30  4:46 UTC (permalink / raw)
  To: Zheng, Lv
  Cc: Alan Cox, Wysocki, Rafael J, Brown, Len, Mika Westerberg,
	linux-acpi, linux-serial, Jiri Slaby (jslaby@suse.cz),
	Heikki Krogerus (heikki.krogerus@linux.intel.com)

On Mon, Jan 28, 2013 at 02:58:00AM +0000, Zheng, Lv wrote:
> Which one do you think is better? V2 or V5 (I have completed the test
> and now I have the V6 ones, I can send them out if you think the
> tty_enum bus is better than the serial_bus).

I don't know, if you have a patch set you want to have reviewed, please
send it, I don't have the time to dig through the links you sent me to
old patch versions, nor should you ever expect someone to do that.

But whatever you send, it better not contain a duplicate of the existing
tty class code, like your last patchset did, that's not going to be
acceptable at all for the reasons I said.

Again, take advantage of your Intel internal Linux kernel developers for
the proper review of your code, and test it!   Your last patch set
showed that this obviously did not happen, and I'm not going to look at
anything further from you until you get a signed-off-by by at least one
of them first (preferably more than one.)

greg k-h

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [RFC PATCH] ACPI / serial: Add UART change_pm support with ACPI power domain
  2012-12-03  3:39 [RFC PATCH 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
                   ` (8 preceding siblings ...)
  2013-01-24 10:30 ` [RFC PATCH v5] HACK: UART: Add dummy devices to test the enumeration Lv Zheng
@ 2013-02-06  6:26 ` Lv Zheng
  2013-02-06 19:07   ` Greg Kroah-Hartman
  2013-04-03  2:05 ` [PATCH v6] ACPI / serial: Add peripheral PnP IDs enumeration support Lv Zheng
  10 siblings, 1 reply; 71+ messages in thread
From: Lv Zheng @ 2013-02-06  6:26 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Alan Cox, Rafael J. Wysocki,
	Len Brown, Mika Westerberg, Krogerus Heikki
  Cc: linux-acpi, linux-serial, Lv Zheng

This patch enables ACPI power domain on UART user devices. With this
patch, Linux can achieve open/close based ACPI power manageability for
UART tty ports.

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/acpi/acpi_uart.c         |   40 ++++++++++++++++++++++++++++++++++++++
 drivers/tty/serial/serial_core.c |    5 +++++
 include/linux/tty.h              |    4 ++++
 3 files changed, 49 insertions(+)

diff --git a/drivers/acpi/acpi_uart.c b/drivers/acpi/acpi_uart.c
index c27fc7c..a6735b1 100644
--- a/drivers/acpi/acpi_uart.c
+++ b/drivers/acpi/acpi_uart.c
@@ -163,3 +163,43 @@ void acpi_uart_register_user_device(struct device *parent,
 	}
 }
 EXPORT_SYMBOL_GPL(acpi_uart_register_user_device);
+
+/**
+ * acpi_uart_user_change_pm - change pm for tty port
+ * @port: the tty port
+ * @power_on: whether or not power on the device
+ *
+ * Change PM for tty user device.
+ */
+void acpi_uart_user_change_pm(struct tty_port *port, bool power_on)
+{
+	acpi_handle handle;
+	struct acpi_device *adev;
+	struct acpi_device_physical_node *entry;
+	struct device *ldev;
+
+	BUG_ON(!port);
+	handle = ACPI_NODE_GET(port->acpi_node);
+	if (!handle)
+		return;
+	if (acpi_bus_get_device(handle, &adev))
+		return;
+	if (acpi_bus_get_status(adev) || !adev->status.present)
+		return;
+
+	list_for_each_entry(entry, &adev->physical_node_list, node) {
+		ldev = get_device(entry->dev);
+		if (!ldev)
+			continue;
+		if (ldev->class == tty_class) {
+			if (power_on)
+				acpi_dev_pm_attach(ldev, true);
+			else
+				acpi_dev_pm_detach(ldev, false);
+			put_device(ldev);
+			return;
+		}
+		put_device(ldev);
+	}
+}
+EXPORT_SYMBOL_GPL(acpi_uart_user_change_pm);
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index c10bb4c..6ca4a5c 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -1901,10 +1901,15 @@ static void uart_change_pm(struct uart_state *state,
 			   enum uart_pm_state pm_state)
 {
 	struct uart_port *port = state->uart_port;
+	struct tty_port *tport = &state->port;
 
 	if (state->pm_state != pm_state) {
+		if (pm_state == UART_PM_STATE_ON)
+			acpi_uart_user_change_pm(tport, true);
 		if (port->ops->pm)
 			port->ops->pm(port, pm_state, state->pm_state);
+		if (pm_state == UART_PM_STATE_OFF)
+			acpi_uart_user_change_pm(tport, false);
 		state->pm_state = pm_state;
 	}
 }
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 5f17ab5..fbf8def 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -687,11 +687,15 @@ void acpi_uart_register_user_device(struct device *parent,
 		struct device *dev,
 		struct tty_driver *driver,
 		unsigned int index);
+extern void acpi_uart_user_change_pm(struct tty_port *port,
+		bool power_on);
 #else
 static inline void acpi_uart_register_user_device(struct device *parent,
 		struct device *dev,
 		struct tty_driver *driver,
 		unsigned int index) {}
+static inline void acpi_uart_user_change_pm(struct tty_port *port,
+		bool power_on) {}
 #endif
 
 #endif
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* Re: [RFC PATCH] ACPI / serial: Add UART change_pm support with ACPI power domain
  2013-02-06  6:26 ` [RFC PATCH] ACPI / serial: Add UART change_pm support with ACPI power domain Lv Zheng
@ 2013-02-06 19:07   ` Greg Kroah-Hartman
  2013-02-06 21:39     ` Rafael J. Wysocki
  0 siblings, 1 reply; 71+ messages in thread
From: Greg Kroah-Hartman @ 2013-02-06 19:07 UTC (permalink / raw)
  To: Lv Zheng
  Cc: Jiri Slaby, Alan Cox, Rafael J. Wysocki, Len Brown,
	Mika Westerberg, Krogerus Heikki, linux-acpi, linux-serial

On Wed, Feb 06, 2013 at 02:26:26PM +0800, Lv Zheng wrote:
> This patch enables ACPI power domain on UART user devices. With this
> patch, Linux can achieve open/close based ACPI power manageability for
> UART tty ports.

So you are going to be sticking acpi_* calls into common core places all
over the kernel for stuff like this?

What's wrong with using the existing pm callbacks that are there for all
platforms to use?

greg k-h

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [RFC PATCH] ACPI / serial: Add UART change_pm support with ACPI power domain
  2013-02-06 21:39     ` Rafael J. Wysocki
@ 2013-02-06 21:38       ` Greg Kroah-Hartman
  2013-02-07  0:38         ` Zheng, Lv
  0 siblings, 1 reply; 71+ messages in thread
From: Greg Kroah-Hartman @ 2013-02-06 21:38 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Lv Zheng, Jiri Slaby, Alan Cox, Len Brown, Mika Westerberg,
	Krogerus Heikki, linux-acpi, linux-serial

On Wed, Feb 06, 2013 at 10:39:09PM +0100, Rafael J. Wysocki wrote:
> On Wednesday, February 06, 2013 11:07:28 AM Greg Kroah-Hartman wrote:
> > On Wed, Feb 06, 2013 at 02:26:26PM +0800, Lv Zheng wrote:
> > > This patch enables ACPI power domain on UART user devices. With this
> > > patch, Linux can achieve open/close based ACPI power manageability for
> > > UART tty ports.
> > 
> > So you are going to be sticking acpi_* calls into common core places all
> > over the kernel for stuff like this?
> > 
> > What's wrong with using the existing pm callbacks that are there for all
> > platforms to use?
> 
> I think that this just hasn't been thought through sufficiently (again).

Ugh.  Ok, I now am going to just quietly drop all intel.com patches to
the tty/serial layer unless they have at least 2 signed-off-by: lines,
one of which is by someone who is "well known" to me as being sane.

greg k-h

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [RFC PATCH] ACPI / serial: Add UART change_pm support with ACPI power domain
  2013-02-06 19:07   ` Greg Kroah-Hartman
@ 2013-02-06 21:39     ` Rafael J. Wysocki
  2013-02-06 21:38       ` Greg Kroah-Hartman
  0 siblings, 1 reply; 71+ messages in thread
From: Rafael J. Wysocki @ 2013-02-06 21:39 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Lv Zheng, Jiri Slaby, Alan Cox, Len Brown, Mika Westerberg,
	Krogerus Heikki, linux-acpi, linux-serial

On Wednesday, February 06, 2013 11:07:28 AM Greg Kroah-Hartman wrote:
> On Wed, Feb 06, 2013 at 02:26:26PM +0800, Lv Zheng wrote:
> > This patch enables ACPI power domain on UART user devices. With this
> > patch, Linux can achieve open/close based ACPI power manageability for
> > UART tty ports.
> 
> So you are going to be sticking acpi_* calls into common core places all
> over the kernel for stuff like this?
> 
> What's wrong with using the existing pm callbacks that are there for all
> platforms to use?

I think that this just hasn't been thought through sufficiently (again).

Sorry about this noise,
Rafael


-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply	[flat|nested] 71+ messages in thread

* RE: [RFC PATCH] ACPI / serial: Add UART change_pm support with ACPI power domain
  2013-02-06 21:38       ` Greg Kroah-Hartman
@ 2013-02-07  0:38         ` Zheng, Lv
  0 siblings, 0 replies; 71+ messages in thread
From: Zheng, Lv @ 2013-02-07  0:38 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Rafael J. Wysocki
  Cc: Jiri Slaby, Alan Cox, Brown, Len, Mika Westerberg, Krogerus,
	Heikki, linux-acpi, linux-serial

> > > So you are going to be sticking acpi_* calls into common core places
> > > all over the kernel for stuff like this?
> > >
> > > What's wrong with using the existing pm callbacks that are there for
> > > all platforms to use?
> >
> > I think that this just hasn't been thought through sufficiently (again).
> 
> Ugh.  Ok, I now am going to just quietly drop all intel.com patches to the
> tty/serial layer unless they have at least 2 signed-off-by: lines, one of which is
> by someone who is "well known" to me as being sane.

Sorry for the noise. Please ignore.
It's sent by wrong typing "gitsendemail acpi-uart" instead of "gitsendemail intel-uart" where gitsendemail is my patch sending script.
It should be reviewed internally prior to be sent to the public, collecting advices about where should be the better place to implement an open/close based PM.

Thanks
-Lv

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH v6] ACPI / serial: Add peripheral PnP IDs enumeration support
  2012-12-03  3:39 [RFC PATCH 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
                   ` (9 preceding siblings ...)
  2013-02-06  6:26 ` [RFC PATCH] ACPI / serial: Add UART change_pm support with ACPI power domain Lv Zheng
@ 2013-04-03  2:05 ` Lv Zheng
  2013-04-03 16:14   ` Greg Kroah-Hartman
  10 siblings, 1 reply; 71+ messages in thread
From: Lv Zheng @ 2013-04-03  2:05 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Rafael J. Wysocki, Len Brown
  Cc: linux-acpi, linux-serial, Lv Zheng, Zhang Rui

ACPI 5.0 specification introduces mechanisms of enumerating the peripherals
connected on the serial buses. It will look like:
  Device (BTH0)
  {
      Name (_HID, "IBMF001")  // _HID: Hardware ID
      Name (_CID, "PNPF001")  // _CID: Compatible ID
      Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
      {
          Name (UBUF, ResourceTemplate ()
          {
              UartSerialBus (0x0001C200, DataBitsEight, StopBitsOne,
                  0xC0, LittleEndian, ParityTypeNone, FlowControlHardware,
                  0x0020, 0x0020, "\\_SB.PCI0.UA00",
                  0x00, ResourceConsumer, ,
              )
          }
          Return (UBUF)
      }
  }
This patch follows the specification, implementing such UART enumeration
machanism by exporting current peripheral HID/CIDs for the UART devices.
A userspace udev rule may look like:
 SUBSYSTEM=tty, ATTR{peripheral_type}=="*:IBMF001:*",
 RUN+="/bin/userprog --devpath=%p"
Thanks for the internal composers:
  Mika Westerberg <mika.westerberg@intel.com>
  Huang Ying <ying.huang@intel.com>
  Zhang Rui <rui.zhang@intel.com>
Thanks for the internal reviewers:
  Alan Cox <alan@linux.intel.com>
  Rafael J. Wysocki <rafael.j.wysocki@intel.com>
  Heikki Krogerus <heikki.krogerus@intel.com>
  Andriy Shevchenko <andriy.shevchenko@intel.com>
  Fengguang Wu <fengguang.wu@intel.com>

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
Acked-by: Huang Ying <ying.huang@intel.com>
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/acpi/Kconfig             |    6 ++
 drivers/acpi/Makefile            |    1 +
 drivers/acpi/acpi_uart.c         |  135 ++++++++++++++++++++++++++++++++++++++
 drivers/acpi/internal.h          |    2 +
 drivers/acpi/scan.c              |    8 +--
 drivers/tty/serial/serial_core.c |   15 +++++
 include/linux/tty.h              |   11 ++++
 7 files changed, 174 insertions(+), 4 deletions(-)
 create mode 100644 drivers/acpi/acpi_uart.c

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 92ed969..42233f6 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -186,6 +186,12 @@ config ACPI_I2C
 	help
 	  ACPI I2C enumeration support.
 
+config ACPI_UART
+	def_tristate TTY
+	depends on TTY
+	help
+	  ACPI UART enumeration support.
+
 config ACPI_PROCESSOR
 	tristate "Processor"
 	select THERMAL
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 474fcfe..4db5470 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS)	+= ec_sys.o
 obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
 obj-$(CONFIG_ACPI_BGRT)		+= bgrt.o
 obj-$(CONFIG_ACPI_I2C)		+= acpi_i2c.o
+obj-$(CONFIG_ACPI_UART)		+= acpi_uart.o
 
 # processor has its own "processor." module_param namespace
 processor-y			:= processor_driver.o processor_throttling.o
diff --git a/drivers/acpi/acpi_uart.c b/drivers/acpi/acpi_uart.c
new file mode 100644
index 0000000..dd5ac2e
--- /dev/null
+++ b/drivers/acpi/acpi_uart.c
@@ -0,0 +1,135 @@
+/*
+ * acpi_uart.c - ACPI UART enumeration support
+ *
+ * Copyright (c) 2013, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ */
+
+#include <linux/init.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+
+#include "internal.h"
+
+struct acpi_uart_buf {
+	char *buf;
+	size_t size;
+	int *len;
+};
+
+struct acpi_uart_enum {
+	void *context;
+	acpi_status status;
+
+	struct acpi_device *adev;
+};
+
+static acpi_status
+acpi_uart_enum_pnpids(struct acpi_device *adev,
+		      struct acpi_resource_uart_serialbus *sb,
+		      void *context);
+
+static int acpi_uart_enum_resources(struct acpi_resource *ares,
+				    void *context)
+{
+	struct acpi_uart_enum *ctx = context;
+	struct acpi_resource_uart_serialbus *sb;
+
+	if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS)
+		return 1;
+	sb = &ares->data.uart_serial_bus;
+	if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_UART)
+		return 1;
+
+	ctx->status = acpi_uart_enum_pnpids(ctx->adev, sb, ctx->context);
+	return 1;
+}
+
+static acpi_status acpi_uart_enum_devices(acpi_handle handle, u32 level,
+					  void *context,
+					  void **return_value)
+{
+	struct acpi_uart_enum *ctx = context;
+	struct acpi_device *adev;
+	struct list_head resource_list;
+
+	if (acpi_bus_get_device(handle, &adev))
+		return AE_OK;
+	if (acpi_bus_get_status(adev) || !adev->status.present)
+		return AE_OK;
+
+	/* Enumerate resources. */
+	ctx->adev = adev;
+	ctx->status = AE_OK;
+	INIT_LIST_HEAD(&resource_list);
+	acpi_dev_get_resources(adev, &resource_list,
+			       acpi_uart_enum_resources, ctx);
+	acpi_dev_free_resource_list(&resource_list);
+
+	return ctx->status;
+}
+
+static int acpi_uart_walk_port(struct device *parent,
+			       void *context)
+{
+	acpi_handle handle;
+	acpi_status status;
+	struct acpi_uart_enum ctx;
+
+	handle = parent ? ACPI_HANDLE(parent) : NULL;
+	if (!handle)
+		return -ENODEV;
+
+	ctx.context = context;
+
+	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
+				     acpi_uart_enum_devices, NULL,
+				     &ctx, NULL);
+	if (ACPI_FAILURE(status))
+		return -EINVAL;
+
+	return 0;
+}
+
+static acpi_status
+acpi_uart_enum_pnpids(struct acpi_device *adev,
+		      struct acpi_resource_uart_serialbus *sb,
+		      void *context)
+{
+	int len;
+	struct acpi_uart_buf *ctx = context;
+
+	len = acpi_device_create_modalias(adev, ctx->buf, ctx->size);
+	*ctx->len = len;
+
+	return len < 0 ? AE_CTRL_DEPTH : AE_OK;
+}
+
+/**
+ * acpi_uart_get_peripheral_type - get peripheral HID/CIDs
+ * @dev: the tty class device
+ * @buf: the string buffer containing HID/CIDs
+ * @size: the size of the string buffer
+ *
+ * Obtain UART peripheral HID/CIDs.  Return buffer size on success,
+ * -errno on failure.
+ */
+int acpi_uart_get_peripheral_type(struct device *dev,
+				  char *buf, size_t size)
+{
+	int ret;
+	int len = 0;
+	struct acpi_uart_buf ctx = { buf, size, &len };
+
+	ret = acpi_uart_walk_port(dev->parent, &ctx);
+	if (ret < 0)
+		return ret;
+
+	return len;
+}
+EXPORT_SYMBOL_GPL(acpi_uart_get_peripheral_type);
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 3c94a73..30d7440 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -61,6 +61,8 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
 			     int type, unsigned long long sta);
 void acpi_device_add_finalize(struct acpi_device *device);
 void acpi_free_ids(struct acpi_device *device);
+int acpi_device_create_modalias(struct acpi_device *acpi_dev,
+				char *modalias, int size);
 
 /* --------------------------------------------------------------------------
                                   Power Resource
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 5e7e991..0d7d49f 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -68,8 +68,8 @@ int acpi_scan_add_handler(struct acpi_scan_handler *handler)
  * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get:
  * char *modalias: "acpi:IBM0001:ACPI0001"
 */
-static int create_modalias(struct acpi_device *acpi_dev, char *modalias,
-			   int size)
+int acpi_device_create_modalias(struct acpi_device *acpi_dev,
+				char *modalias, int size)
 {
 	int len;
 	int count;
@@ -99,7 +99,7 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha
 	int len;
 
 	/* Device has no HID and no CID or string is >1024 */
-	len = create_modalias(acpi_dev, buf, 1024);
+	len = acpi_device_create_modalias(acpi_dev, buf, 1024);
 	if (len <= 0)
 		return 0;
 	buf[len++] = '\n';
@@ -567,7 +567,7 @@ static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env)
 
 	if (add_uevent_var(env, "MODALIAS="))
 		return -ENOMEM;
-	len = create_modalias(acpi_dev, &env->buf[env->buflen - 1],
+	len = acpi_device_create_modalias(acpi_dev, &env->buf[env->buflen - 1],
 			      sizeof(env->buf) - env->buflen);
 	if (len >= (sizeof(env->buf) - env->buflen))
 		return -ENOMEM;
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index a400002..17e104e 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -2515,6 +2515,19 @@ static ssize_t uart_get_attr_iomem_reg_shift(struct device *dev,
 	return snprintf(buf, PAGE_SIZE, "%d\n", tmp.iomem_reg_shift);
 }
 
+static ssize_t uart_get_attr_peripheral_type(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	int len;
+
+	len = acpi_uart_get_peripheral_type(dev, buf, 1024);
+	if (len > 0) {
+		buf[len++] = '\n';
+		return len;
+	}
+	return snprintf(buf, PAGE_SIZE, "uart\n");
+}
+
 static DEVICE_ATTR(type, S_IRUSR | S_IRGRP, uart_get_attr_type, NULL);
 static DEVICE_ATTR(line, S_IRUSR | S_IRGRP, uart_get_attr_line, NULL);
 static DEVICE_ATTR(port, S_IRUSR | S_IRGRP, uart_get_attr_port, NULL);
@@ -2528,6 +2541,7 @@ static DEVICE_ATTR(custom_divisor, S_IRUSR | S_IRGRP, uart_get_attr_custom_divis
 static DEVICE_ATTR(io_type, S_IRUSR | S_IRGRP, uart_get_attr_io_type, NULL);
 static DEVICE_ATTR(iomem_base, S_IRUSR | S_IRGRP, uart_get_attr_iomem_base, NULL);
 static DEVICE_ATTR(iomem_reg_shift, S_IRUSR | S_IRGRP, uart_get_attr_iomem_reg_shift, NULL);
+static DEVICE_ATTR(peripheral_type, S_IRUSR | S_IRGRP, uart_get_attr_peripheral_type, NULL);
 
 static struct attribute *tty_dev_attrs[] = {
 	&dev_attr_type.attr,
@@ -2543,6 +2557,7 @@ static struct attribute *tty_dev_attrs[] = {
 	&dev_attr_io_type.attr,
 	&dev_attr_iomem_base.attr,
 	&dev_attr_iomem_reg_shift.attr,
+	&dev_attr_peripheral_type.attr,
 	NULL,
 	};
 
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 367a9df..723fdd01 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -692,4 +692,15 @@ do {									\
 } while (0)
 
 
+#ifdef CONFIG_ACPI_UART
+int acpi_uart_get_peripheral_type(struct device *dev,
+	char *buf, size_t size);
+#else
+static inline int acpi_uart_get_peripheral_type(struct device *dev,
+	char *buf, size_t size)
+{
+	return -ENODEV;
+}
+#endif
+
 #endif
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* Re: [PATCH v6] ACPI / serial: Add peripheral PnP IDs enumeration support
  2013-04-03  2:05 ` [PATCH v6] ACPI / serial: Add peripheral PnP IDs enumeration support Lv Zheng
@ 2013-04-03 16:14   ` Greg Kroah-Hartman
  2013-04-04 10:12     ` Zheng, Lv
  2013-04-04 15:43     ` Zheng, Lv
  0 siblings, 2 replies; 71+ messages in thread
From: Greg Kroah-Hartman @ 2013-04-03 16:14 UTC (permalink / raw)
  To: Lv Zheng
  Cc: Rafael J. Wysocki, Len Brown, linux-acpi, linux-serial, Zhang Rui

On Wed, Apr 03, 2013 at 10:05:49AM +0800, Lv Zheng wrote:
> ACPI 5.0 specification introduces mechanisms of enumerating the peripherals
> connected on the serial buses. It will look like:
>   Device (BTH0)
>   {
>       Name (_HID, "IBMF001")  // _HID: Hardware ID
>       Name (_CID, "PNPF001")  // _CID: Compatible ID
>       Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
>       {
>           Name (UBUF, ResourceTemplate ()
>           {
>               UartSerialBus (0x0001C200, DataBitsEight, StopBitsOne,
>                   0xC0, LittleEndian, ParityTypeNone, FlowControlHardware,
>                   0x0020, 0x0020, "\\_SB.PCI0.UA00",
>                   0x00, ResourceConsumer, ,
>               )
>           }
>           Return (UBUF)
>       }
>   }
> This patch follows the specification, implementing such UART enumeration
> machanism by exporting current peripheral HID/CIDs for the UART devices.
> A userspace udev rule may look like:
>  SUBSYSTEM=tty, ATTR{peripheral_type}=="*:IBMF001:*",
>  RUN+="/bin/userprog --devpath=%p"
> Thanks for the internal composers:
>   Mika Westerberg <mika.westerberg@intel.com>
>   Huang Ying <ying.huang@intel.com>
>   Zhang Rui <rui.zhang@intel.com>
> Thanks for the internal reviewers:
>   Alan Cox <alan@linux.intel.com>
>   Rafael J. Wysocki <rafael.j.wysocki@intel.com>
>   Heikki Krogerus <heikki.krogerus@intel.com>
>   Andriy Shevchenko <andriy.shevchenko@intel.com>
>   Fengguang Wu <fengguang.wu@intel.com>

Why are these names not on the list below?

> Signed-off-by: Lv Zheng <lv.zheng@intel.com>
> Signed-off-by: Zhang Rui <rui.zhang@intel.com>
> Acked-by: Huang Ying <ying.huang@intel.com>
> Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>


> ---
>  drivers/acpi/Kconfig             |    6 ++
>  drivers/acpi/Makefile            |    1 +
>  drivers/acpi/acpi_uart.c         |  135 ++++++++++++++++++++++++++++++++++++++
>  drivers/acpi/internal.h          |    2 +
>  drivers/acpi/scan.c              |    8 +--
>  drivers/tty/serial/serial_core.c |   15 +++++
>  include/linux/tty.h              |   11 ++++
>  7 files changed, 174 insertions(+), 4 deletions(-)
>  create mode 100644 drivers/acpi/acpi_uart.c
> 
> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
> index 92ed969..42233f6 100644
> --- a/drivers/acpi/Kconfig
> +++ b/drivers/acpi/Kconfig
> @@ -186,6 +186,12 @@ config ACPI_I2C
>  	help
>  	  ACPI I2C enumeration support.
>  
> +config ACPI_UART
> +	def_tristate TTY
> +	depends on TTY
> +	help
> +	  ACPI UART enumeration support.

Please provide more information here.

As this is primarily an ACPI patch, I need an ack from a ACPI maintainer
before I can take this.

> +
>  config ACPI_PROCESSOR
>  	tristate "Processor"
>  	select THERMAL
> diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
> index 474fcfe..4db5470 100644
> --- a/drivers/acpi/Makefile
> +++ b/drivers/acpi/Makefile
> @@ -72,6 +72,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS)	+= ec_sys.o
>  obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
>  obj-$(CONFIG_ACPI_BGRT)		+= bgrt.o
>  obj-$(CONFIG_ACPI_I2C)		+= acpi_i2c.o
> +obj-$(CONFIG_ACPI_UART)		+= acpi_uart.o
>  
>  # processor has its own "processor." module_param namespace
>  processor-y			:= processor_driver.o processor_throttling.o
> diff --git a/drivers/acpi/acpi_uart.c b/drivers/acpi/acpi_uart.c
> new file mode 100644
> index 0000000..dd5ac2e
> --- /dev/null
> +++ b/drivers/acpi/acpi_uart.c
> @@ -0,0 +1,135 @@
> +/*
> + * acpi_uart.c - ACPI UART enumeration support
> + *
> + * Copyright (c) 2013, Intel Corporation
> + * Author: Lv Zheng <lv.zheng@intel.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + */
> +
> +#include <linux/init.h>
> +#include <linux/tty.h>
> +#include <linux/module.h>
> +#include <linux/acpi.h>
> +
> +#include "internal.h"
> +
> +struct acpi_uart_buf {
> +	char *buf;
> +	size_t size;
> +	int *len;
> +};
> +
> +struct acpi_uart_enum {
> +	void *context;
> +	acpi_status status;
> +
> +	struct acpi_device *adev;
> +};
> +
> +static acpi_status
> +acpi_uart_enum_pnpids(struct acpi_device *adev,
> +		      struct acpi_resource_uart_serialbus *sb,
> +		      void *context);
> +
> +static int acpi_uart_enum_resources(struct acpi_resource *ares,
> +				    void *context)
> +{
> +	struct acpi_uart_enum *ctx = context;
> +	struct acpi_resource_uart_serialbus *sb;
> +
> +	if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS)
> +		return 1;
> +	sb = &ares->data.uart_serial_bus;
> +	if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_UART)
> +		return 1;
> +
> +	ctx->status = acpi_uart_enum_pnpids(ctx->adev, sb, ctx->context);
> +	return 1;
> +}
> +
> +static acpi_status acpi_uart_enum_devices(acpi_handle handle, u32 level,
> +					  void *context,
> +					  void **return_value)
> +{
> +	struct acpi_uart_enum *ctx = context;
> +	struct acpi_device *adev;
> +	struct list_head resource_list;
> +
> +	if (acpi_bus_get_device(handle, &adev))
> +		return AE_OK;
> +	if (acpi_bus_get_status(adev) || !adev->status.present)
> +		return AE_OK;
> +
> +	/* Enumerate resources. */
> +	ctx->adev = adev;
> +	ctx->status = AE_OK;
> +	INIT_LIST_HEAD(&resource_list);
> +	acpi_dev_get_resources(adev, &resource_list,
> +			       acpi_uart_enum_resources, ctx);
> +	acpi_dev_free_resource_list(&resource_list);
> +
> +	return ctx->status;
> +}
> +
> +static int acpi_uart_walk_port(struct device *parent,
> +			       void *context)
> +{
> +	acpi_handle handle;
> +	acpi_status status;
> +	struct acpi_uart_enum ctx;
> +
> +	handle = parent ? ACPI_HANDLE(parent) : NULL;
> +	if (!handle)
> +		return -ENODEV;
> +
> +	ctx.context = context;
> +
> +	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
> +				     acpi_uart_enum_devices, NULL,
> +				     &ctx, NULL);
> +	if (ACPI_FAILURE(status))
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static acpi_status
> +acpi_uart_enum_pnpids(struct acpi_device *adev,
> +		      struct acpi_resource_uart_serialbus *sb,
> +		      void *context)
> +{
> +	int len;
> +	struct acpi_uart_buf *ctx = context;
> +
> +	len = acpi_device_create_modalias(adev, ctx->buf, ctx->size);
> +	*ctx->len = len;
> +
> +	return len < 0 ? AE_CTRL_DEPTH : AE_OK;
> +}
> +
> +/**
> + * acpi_uart_get_peripheral_type - get peripheral HID/CIDs
> + * @dev: the tty class device
> + * @buf: the string buffer containing HID/CIDs
> + * @size: the size of the string buffer
> + *
> + * Obtain UART peripheral HID/CIDs.  Return buffer size on success,
> + * -errno on failure.
> + */
> +int acpi_uart_get_peripheral_type(struct device *dev,
> +				  char *buf, size_t size)
> +{
> +	int ret;
> +	int len = 0;
> +	struct acpi_uart_buf ctx = { buf, size, &len };
> +
> +	ret = acpi_uart_walk_port(dev->parent, &ctx);
> +	if (ret < 0)
> +		return ret;
> +
> +	return len;
> +}
> +EXPORT_SYMBOL_GPL(acpi_uart_get_peripheral_type);
> diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
> index 3c94a73..30d7440 100644
> --- a/drivers/acpi/internal.h
> +++ b/drivers/acpi/internal.h
> @@ -61,6 +61,8 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
>  			     int type, unsigned long long sta);
>  void acpi_device_add_finalize(struct acpi_device *device);
>  void acpi_free_ids(struct acpi_device *device);
> +int acpi_device_create_modalias(struct acpi_device *acpi_dev,
> +				char *modalias, int size);
>  
>  /* --------------------------------------------------------------------------
>                                    Power Resource
> diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
> index 5e7e991..0d7d49f 100644
> --- a/drivers/acpi/scan.c
> +++ b/drivers/acpi/scan.c
> @@ -68,8 +68,8 @@ int acpi_scan_add_handler(struct acpi_scan_handler *handler)
>   * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get:
>   * char *modalias: "acpi:IBM0001:ACPI0001"
>  */
> -static int create_modalias(struct acpi_device *acpi_dev, char *modalias,
> -			   int size)
> +int acpi_device_create_modalias(struct acpi_device *acpi_dev,
> +				char *modalias, int size)
>  {
>  	int len;
>  	int count;
> @@ -99,7 +99,7 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha
>  	int len;
>  
>  	/* Device has no HID and no CID or string is >1024 */
> -	len = create_modalias(acpi_dev, buf, 1024);
> +	len = acpi_device_create_modalias(acpi_dev, buf, 1024);
>  	if (len <= 0)
>  		return 0;
>  	buf[len++] = '\n';
> @@ -567,7 +567,7 @@ static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env)
>  
>  	if (add_uevent_var(env, "MODALIAS="))
>  		return -ENOMEM;
> -	len = create_modalias(acpi_dev, &env->buf[env->buflen - 1],
> +	len = acpi_device_create_modalias(acpi_dev, &env->buf[env->buflen - 1],
>  			      sizeof(env->buf) - env->buflen);
>  	if (len >= (sizeof(env->buf) - env->buflen))
>  		return -ENOMEM;
> diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
> index a400002..17e104e 100644
> --- a/drivers/tty/serial/serial_core.c
> +++ b/drivers/tty/serial/serial_core.c
> @@ -2515,6 +2515,19 @@ static ssize_t uart_get_attr_iomem_reg_shift(struct device *dev,
>  	return snprintf(buf, PAGE_SIZE, "%d\n", tmp.iomem_reg_shift);
>  }
>  
> +static ssize_t uart_get_attr_peripheral_type(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	int len;
> +
> +	len = acpi_uart_get_peripheral_type(dev, buf, 1024);
> +	if (len > 0) {
> +		buf[len++] = '\n';
> +		return len;
> +	}
> +	return snprintf(buf, PAGE_SIZE, "uart\n");

What is the "uart" part for?  Isn't that kind of obvious?

> +}

You are creating a new sysfs file, you must create a new
Documentation/ABI entry as well.

> +
>  static DEVICE_ATTR(type, S_IRUSR | S_IRGRP, uart_get_attr_type, NULL);
>  static DEVICE_ATTR(line, S_IRUSR | S_IRGRP, uart_get_attr_line, NULL);
>  static DEVICE_ATTR(port, S_IRUSR | S_IRGRP, uart_get_attr_port, NULL);
> @@ -2528,6 +2541,7 @@ static DEVICE_ATTR(custom_divisor, S_IRUSR | S_IRGRP, uart_get_attr_custom_divis
>  static DEVICE_ATTR(io_type, S_IRUSR | S_IRGRP, uart_get_attr_io_type, NULL);
>  static DEVICE_ATTR(iomem_base, S_IRUSR | S_IRGRP, uart_get_attr_iomem_base, NULL);
>  static DEVICE_ATTR(iomem_reg_shift, S_IRUSR | S_IRGRP, uart_get_attr_iomem_reg_shift, NULL);
> +static DEVICE_ATTR(peripheral_type, S_IRUSR | S_IRGRP, uart_get_attr_peripheral_type, NULL);
>  
>  static struct attribute *tty_dev_attrs[] = {
>  	&dev_attr_type.attr,
> @@ -2543,6 +2557,7 @@ static struct attribute *tty_dev_attrs[] = {
>  	&dev_attr_io_type.attr,
>  	&dev_attr_iomem_base.attr,
>  	&dev_attr_iomem_reg_shift.attr,
> +	&dev_attr_peripheral_type.attr,
>  	NULL,
>  	};
>  
> diff --git a/include/linux/tty.h b/include/linux/tty.h
> index 367a9df..723fdd01 100644
> --- a/include/linux/tty.h
> +++ b/include/linux/tty.h
> @@ -692,4 +692,15 @@ do {									\
>  } while (0)
>  
>  
> +#ifdef CONFIG_ACPI_UART
> +int acpi_uart_get_peripheral_type(struct device *dev,
> +	char *buf, size_t size);
> +#else
> +static inline int acpi_uart_get_peripheral_type(struct device *dev,
> +	char *buf, size_t size)
> +{
> +	return -ENODEV;
> +}
> +#endif

I see you never tested this code without the configuration option
enabled :(

{sigh}

greg k-h

^ permalink raw reply	[flat|nested] 71+ messages in thread

* RE: [PATCH v6] ACPI / serial: Add peripheral PnP IDs enumeration support
  2013-04-03 16:14   ` Greg Kroah-Hartman
@ 2013-04-04 10:12     ` Zheng, Lv
  2013-04-07  3:05       ` Zheng, Lv
  2013-04-04 15:43     ` Zheng, Lv
  1 sibling, 1 reply; 71+ messages in thread
From: Zheng, Lv @ 2013-04-04 10:12 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Wysocki, Rafael J, Brown, Len, linux-acpi, linux-serial, Zhang, Rui

> > ACPI 5.0 specification introduces mechanisms of enumerating the
> > peripherals connected on the serial buses. It will look like:
> >   Device (BTH0)
> >   {
> >       Name (_HID, "IBMF001")  // _HID: Hardware ID
> >       Name (_CID, "PNPF001")  // _CID: Compatible ID
> >       Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
> >       {
> >           Name (UBUF, ResourceTemplate ()
> >           {
> >               UartSerialBus (0x0001C200, DataBitsEight, StopBitsOne,
> >                   0xC0, LittleEndian, ParityTypeNone,
> FlowControlHardware,
> >                   0x0020, 0x0020, "\\_SB.PCI0.UA00",
> >                   0x00, ResourceConsumer, ,
> >               )
> >           }
> >           Return (UBUF)
> >       }
> >   }
> > This patch follows the specification, implementing such UART
> > enumeration machanism by exporting current peripheral HID/CIDs for the
> UART devices.
> > A userspace udev rule may look like:
> >  SUBSYSTEM=tty, ATTR{peripheral_type}=="*:IBMF001:*",
> >  RUN+="/bin/userprog --devpath=%p"
> > Thanks for the internal composers:
> >   Mika Westerberg <mika.westerberg@intel.com>
> >   Huang Ying <ying.huang@intel.com>
> >   Zhang Rui <rui.zhang@intel.com>
> > Thanks for the internal reviewers:
> >   Alan Cox <alan@linux.intel.com>
> >   Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> >   Heikki Krogerus <heikki.krogerus@intel.com>
> >   Andriy Shevchenko <andriy.shevchenko@intel.com>
> >   Fengguang Wu <fengguang.wu@intel.com>
> 
> Why are these names not on the list below?

I obtained help from them on old revisions.
Mika is the key person for the acpi serial enumeration.
He has implemented the acpi i2c/spi serial enumeration.

> > Signed-off-by: Lv Zheng <lv.zheng@intel.com>
> > Signed-off-by: Zhang Rui <rui.zhang@intel.com>
> > Acked-by: Huang Ying <ying.huang@intel.com>
> > Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
> 
> 
> > ---
> >  drivers/acpi/Kconfig             |    6 ++
> >  drivers/acpi/Makefile            |    1 +
> >  drivers/acpi/acpi_uart.c         |  135
> ++++++++++++++++++++++++++++++++++++++
> >  drivers/acpi/internal.h          |    2 +
> >  drivers/acpi/scan.c              |    8 +--
> >  drivers/tty/serial/serial_core.c |   15 +++++
> >  include/linux/tty.h              |   11 ++++
> >  7 files changed, 174 insertions(+), 4 deletions(-)  create mode
> > 100644 drivers/acpi/acpi_uart.c
> >
> > diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index
> > 92ed969..42233f6 100644
> > --- a/drivers/acpi/Kconfig
> > +++ b/drivers/acpi/Kconfig
> > @@ -186,6 +186,12 @@ config ACPI_I2C
> >  	help
> >  	  ACPI I2C enumeration support.
> >
> > +config ACPI_UART
> > +	def_tristate TTY
> > +	depends on TTY
> > +	help
> > +	  ACPI UART enumeration support.
> 
> Please provide more information here.

OK.

> As this is primarily an ACPI patch, I need an ack from a ACPI maintainer before I
> can take this.

OK, I'll ask for this.

> > +
> >  config ACPI_PROCESSOR
> >  	tristate "Processor"
> >  	select THERMAL
> > diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index
> > 474fcfe..4db5470 100644
> > --- a/drivers/acpi/Makefile
> > +++ b/drivers/acpi/Makefile
> > @@ -72,6 +72,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS)	+= ec_sys.o
> >  obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
> >  obj-$(CONFIG_ACPI_BGRT)		+= bgrt.o
> >  obj-$(CONFIG_ACPI_I2C)		+= acpi_i2c.o
> > +obj-$(CONFIG_ACPI_UART)		+= acpi_uart.o
> >
> >  # processor has its own "processor." module_param namespace
> >  processor-y			:= processor_driver.o processor_throttling.o
> > diff --git a/drivers/acpi/acpi_uart.c b/drivers/acpi/acpi_uart.c new
> > file mode 100644 index 0000000..dd5ac2e
> > --- /dev/null
> > +++ b/drivers/acpi/acpi_uart.c
> > @@ -0,0 +1,135 @@
> > +/*
> > + * acpi_uart.c - ACPI UART enumeration support
> > + *
> > + * Copyright (c) 2013, Intel Corporation
> > + * Author: Lv Zheng <lv.zheng@intel.com>
> > + *
> > + * This program is free software; you can redistribute it and/or
> > +modify it
> > + * under the terms of the GNU General Public License as published by
> > +the
> > + * Free Software Foundation; version 2 of the License.
> > + */
> > +
> > +#include <linux/init.h>
> > +#include <linux/tty.h>
> > +#include <linux/module.h>
> > +#include <linux/acpi.h>
> > +
> > +#include "internal.h"
> > +
> > +struct acpi_uart_buf {
> > +	char *buf;
> > +	size_t size;
> > +	int *len;
> > +};
> > +
> > +struct acpi_uart_enum {
> > +	void *context;
> > +	acpi_status status;
> > +
> > +	struct acpi_device *adev;
> > +};
> > +
> > +static acpi_status
> > +acpi_uart_enum_pnpids(struct acpi_device *adev,
> > +		      struct acpi_resource_uart_serialbus *sb,
> > +		      void *context);
> > +
> > +static int acpi_uart_enum_resources(struct acpi_resource *ares,
> > +				    void *context)
> > +{
> > +	struct acpi_uart_enum *ctx = context;
> > +	struct acpi_resource_uart_serialbus *sb;
> > +
> > +	if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS)
> > +		return 1;
> > +	sb = &ares->data.uart_serial_bus;
> > +	if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_UART)
> > +		return 1;
> > +
> > +	ctx->status = acpi_uart_enum_pnpids(ctx->adev, sb, ctx->context);
> > +	return 1;
> > +}
> > +
> > +static acpi_status acpi_uart_enum_devices(acpi_handle handle, u32 level,
> > +					  void *context,
> > +					  void **return_value)
> > +{
> > +	struct acpi_uart_enum *ctx = context;
> > +	struct acpi_device *adev;
> > +	struct list_head resource_list;
> > +
> > +	if (acpi_bus_get_device(handle, &adev))
> > +		return AE_OK;
> > +	if (acpi_bus_get_status(adev) || !adev->status.present)
> > +		return AE_OK;
> > +
> > +	/* Enumerate resources. */
> > +	ctx->adev = adev;
> > +	ctx->status = AE_OK;
> > +	INIT_LIST_HEAD(&resource_list);
> > +	acpi_dev_get_resources(adev, &resource_list,
> > +			       acpi_uart_enum_resources, ctx);
> > +	acpi_dev_free_resource_list(&resource_list);
> > +
> > +	return ctx->status;
> > +}
> > +
> > +static int acpi_uart_walk_port(struct device *parent,
> > +			       void *context)
> > +{
> > +	acpi_handle handle;
> > +	acpi_status status;
> > +	struct acpi_uart_enum ctx;
> > +
> > +	handle = parent ? ACPI_HANDLE(parent) : NULL;
> > +	if (!handle)
> > +		return -ENODEV;
> > +
> > +	ctx.context = context;
> > +
> > +	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
> > +				     acpi_uart_enum_devices, NULL,
> > +				     &ctx, NULL);
> > +	if (ACPI_FAILURE(status))
> > +		return -EINVAL;
> > +
> > +	return 0;
> > +}
> > +
> > +static acpi_status
> > +acpi_uart_enum_pnpids(struct acpi_device *adev,
> > +		      struct acpi_resource_uart_serialbus *sb,
> > +		      void *context)
> > +{
> > +	int len;
> > +	struct acpi_uart_buf *ctx = context;
> > +
> > +	len = acpi_device_create_modalias(adev, ctx->buf, ctx->size);
> > +	*ctx->len = len;
> > +
> > +	return len < 0 ? AE_CTRL_DEPTH : AE_OK; }
> > +
> > +/**
> > + * acpi_uart_get_peripheral_type - get peripheral HID/CIDs
> > + * @dev: the tty class device
> > + * @buf: the string buffer containing HID/CIDs
> > + * @size: the size of the string buffer
> > + *
> > + * Obtain UART peripheral HID/CIDs.  Return buffer size on success,
> > + * -errno on failure.
> > + */
> > +int acpi_uart_get_peripheral_type(struct device *dev,
> > +				  char *buf, size_t size)
> > +{
> > +	int ret;
> > +	int len = 0;
> > +	struct acpi_uart_buf ctx = { buf, size, &len };
> > +
> > +	ret = acpi_uart_walk_port(dev->parent, &ctx);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	return len;
> > +}
> > +EXPORT_SYMBOL_GPL(acpi_uart_get_peripheral_type);
> > diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index
> > 3c94a73..30d7440 100644
> > --- a/drivers/acpi/internal.h
> > +++ b/drivers/acpi/internal.h
> > @@ -61,6 +61,8 @@ void acpi_init_device_object(struct acpi_device *device,
> acpi_handle handle,
> >  			     int type, unsigned long long sta);  void
> > acpi_device_add_finalize(struct acpi_device *device);  void
> > acpi_free_ids(struct acpi_device *device);
> > +int acpi_device_create_modalias(struct acpi_device *acpi_dev,
> > +				char *modalias, int size);
> >
> >  /* --------------------------------------------------------------------------
> >                                    Power Resource diff --git
> > a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 5e7e991..0d7d49f
> > 100644
> > --- a/drivers/acpi/scan.c
> > +++ b/drivers/acpi/scan.c
> > @@ -68,8 +68,8 @@ int acpi_scan_add_handler(struct acpi_scan_handler
> *handler)
> >   * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get:
> >   * char *modalias: "acpi:IBM0001:ACPI0001"
> >  */
> > -static int create_modalias(struct acpi_device *acpi_dev, char *modalias,
> > -			   int size)
> > +int acpi_device_create_modalias(struct acpi_device *acpi_dev,
> > +				char *modalias, int size)
> >  {
> >  	int len;
> >  	int count;
> > @@ -99,7 +99,7 @@ acpi_device_modalias_show(struct device *dev, struct
> device_attribute *attr, cha
> >  	int len;
> >
> >  	/* Device has no HID and no CID or string is >1024 */
> > -	len = create_modalias(acpi_dev, buf, 1024);
> > +	len = acpi_device_create_modalias(acpi_dev, buf, 1024);
> >  	if (len <= 0)
> >  		return 0;
> >  	buf[len++] = '\n';
> > @@ -567,7 +567,7 @@ static int acpi_device_uevent(struct device *dev,
> > struct kobj_uevent_env *env)
> >
> >  	if (add_uevent_var(env, "MODALIAS="))
> >  		return -ENOMEM;
> > -	len = create_modalias(acpi_dev, &env->buf[env->buflen - 1],
> > +	len = acpi_device_create_modalias(acpi_dev, &env->buf[env->buflen -
> > +1],
> >  			      sizeof(env->buf) - env->buflen);
> >  	if (len >= (sizeof(env->buf) - env->buflen))
> >  		return -ENOMEM;
> > diff --git a/drivers/tty/serial/serial_core.c
> > b/drivers/tty/serial/serial_core.c
> > index a400002..17e104e 100644
> > --- a/drivers/tty/serial/serial_core.c
> > +++ b/drivers/tty/serial/serial_core.c
> > @@ -2515,6 +2515,19 @@ static ssize_t
> uart_get_attr_iomem_reg_shift(struct device *dev,
> >  	return snprintf(buf, PAGE_SIZE, "%d\n", tmp.iomem_reg_shift);  }
> >
> > +static ssize_t uart_get_attr_peripheral_type(struct device *dev,
> > +		struct device_attribute *attr, char *buf) {
> > +	int len;
> > +
> > +	len = acpi_uart_get_peripheral_type(dev, buf, 1024);
> > +	if (len > 0) {
> > +		buf[len++] = '\n';
> > +		return len;
> > +	}
> > +	return snprintf(buf, PAGE_SIZE, "uart\n");
> 
> What is the "uart" part for?  Isn't that kind of obvious?

It is the default "peripheral_type" content.
Before sending next revision out, let me ask a question here:
Shall I leave this empty as my previous revisions?

> > +}
> 
> You are creating a new sysfs file, you must create a new Documentation/ABI
> entry as well.

OK.

> > +
> >  static DEVICE_ATTR(type, S_IRUSR | S_IRGRP, uart_get_attr_type,
> > NULL);  static DEVICE_ATTR(line, S_IRUSR | S_IRGRP,
> > uart_get_attr_line, NULL);  static DEVICE_ATTR(port, S_IRUSR |
> > S_IRGRP, uart_get_attr_port, NULL); @@ -2528,6 +2541,7 @@ static
> > DEVICE_ATTR(custom_divisor, S_IRUSR | S_IRGRP,
> > uart_get_attr_custom_divis  static DEVICE_ATTR(io_type, S_IRUSR |
> > S_IRGRP, uart_get_attr_io_type, NULL);  static DEVICE_ATTR(iomem_base,
> > S_IRUSR | S_IRGRP, uart_get_attr_iomem_base, NULL);  static
> > DEVICE_ATTR(iomem_reg_shift, S_IRUSR | S_IRGRP,
> > uart_get_attr_iomem_reg_shift, NULL);
> > +static DEVICE_ATTR(peripheral_type, S_IRUSR | S_IRGRP,
> > +uart_get_attr_peripheral_type, NULL);
> >
> >  static struct attribute *tty_dev_attrs[] = {
> >  	&dev_attr_type.attr,
> > @@ -2543,6 +2557,7 @@ static struct attribute *tty_dev_attrs[] = {
> >  	&dev_attr_io_type.attr,
> >  	&dev_attr_iomem_base.attr,
> >  	&dev_attr_iomem_reg_shift.attr,
> > +	&dev_attr_peripheral_type.attr,
> >  	NULL,
> >  	};
> >
> > diff --git a/include/linux/tty.h b/include/linux/tty.h index
> > 367a9df..723fdd01 100644
> > --- a/include/linux/tty.h
> > +++ b/include/linux/tty.h
> > @@ -692,4 +692,15 @@ do {									\
> >  } while (0)
> >
> >
> > +#ifdef CONFIG_ACPI_UART
> > +int acpi_uart_get_peripheral_type(struct device *dev,
> > +	char *buf, size_t size);
> > +#else
> > +static inline int acpi_uart_get_peripheral_type(struct device *dev,
> > +	char *buf, size_t size)
> > +{
> > +	return -ENODEV;
> > +}
> > +#endif
> 
> I see you never tested this code without the configuration option enabled :(
> 
> {sigh}

Yes, I rely too much on an internal test system as it can do this for me automatically.
OK, I'll take care of this next time.
I've a plan and a TODO to offer a complete test framework for this as I've done 10 years ago.
Hope you will like it.

Thanks and best regards
-Lv


^ permalink raw reply	[flat|nested] 71+ messages in thread

* RE: [PATCH v6] ACPI / serial: Add peripheral PnP IDs enumeration support
  2013-04-03 16:14   ` Greg Kroah-Hartman
  2013-04-04 10:12     ` Zheng, Lv
@ 2013-04-04 15:43     ` Zheng, Lv
  2013-04-04 18:23       ` Greg Kroah-Hartman
  1 sibling, 1 reply; 71+ messages in thread
From: Zheng, Lv @ 2013-04-04 15:43 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Wysocki, Rafael J, Brown, Len, linux-acpi, linux-serial, Zhang, Rui

Hi,

> > > +	return snprintf(buf, PAGE_SIZE, "uart\n");
> >
> > What is the "uart" part for?  Isn't that kind of obvious?
> 
> It is the default "peripheral_type" content.
> Before sending next revision out, let me ask a question here:
> Shall I leave this empty as my previous revisions?

Hmm, if this is empty by default then the udev rules may require additional handlings.
Can I use "NONE" here for the user space users?

Thanks and best regards
-Lv


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v6] ACPI / serial: Add peripheral PnP IDs enumeration support
  2013-04-04 15:43     ` Zheng, Lv
@ 2013-04-04 18:23       ` Greg Kroah-Hartman
  2013-04-07  3:11         ` Zheng, Lv
  0 siblings, 1 reply; 71+ messages in thread
From: Greg Kroah-Hartman @ 2013-04-04 18:23 UTC (permalink / raw)
  To: Zheng, Lv
  Cc: Wysocki, Rafael J, Brown, Len, linux-acpi, linux-serial, Zhang, Rui

On Thu, Apr 04, 2013 at 03:43:43PM +0000, Zheng, Lv wrote:
> Hi,
> 
> > > > +	return snprintf(buf, PAGE_SIZE, "uart\n");
> > >
> > > What is the "uart" part for?  Isn't that kind of obvious?
> > 
> > It is the default "peripheral_type" content.
> > Before sending next revision out, let me ask a question here:
> > Shall I leave this empty as my previous revisions?
> 
> Hmm, if this is empty by default then the udev rules may require additional handlings.
> Can I use "NONE" here for the user space users?

No, just don't create the sysfs file at all for that type of device.

greg k-h

^ permalink raw reply	[flat|nested] 71+ messages in thread

* RE: [PATCH v6] ACPI / serial: Add peripheral PnP IDs enumeration support
  2013-04-04 10:12     ` Zheng, Lv
@ 2013-04-07  3:05       ` Zheng, Lv
  0 siblings, 0 replies; 71+ messages in thread
From: Zheng, Lv @ 2013-04-07  3:05 UTC (permalink / raw)
  To: Zheng, Lv, Greg Kroah-Hartman
  Cc: Wysocki, Rafael J, Brown, Len, linux-acpi, linux-serial, Zhang, Rui

Hi, Greg

I think I may misunderstand what you mean here, let me confirm again.
Sorry for doing this.

> > > +#ifdef CONFIG_ACPI_UART
> > > +int acpi_uart_get_peripheral_type(struct device *dev,
> > > +	char *buf, size_t size);
> > > +#else
> > > +static inline int acpi_uart_get_peripheral_type(struct device *dev,
> > > +	char *buf, size_t size)
> > > +{
> > > +	return -ENODEV;
> > > +}
> > > +#endif

> > I see you never tested this code without the configuration option enabled :(

I was thinking you were talking about test plans.
Since I didn't see any issues here and I just didn't want to argue on this topic and replied in this way. :-)
The only user - uart_get_attr_peripheral_type with "int" return value check can ensure no issues here.
> > > +static ssize_t uart_get_attr_peripheral_type(struct device *dev,
> > > +		struct device_attribute *attr, char *buf) {
> > > +	int len;
> > > +
> > > +	len = acpi_uart_get_peripheral_type(dev, buf, 1024);
> > > +	if (len > 0) {
> > > +		buf[len++] = '\n';
> > > +		return len;
> > > +	}
> > > +	return snprintf(buf, PAGE_SIZE, "uart\n");

Now I realize that you might be talking about the "-NODEV" return value is not a good choice, "0" or something else is better.
Shall I return 0 here?

Thanks and best regards
-Lv


^ permalink raw reply	[flat|nested] 71+ messages in thread

* RE: [PATCH v6] ACPI / serial: Add peripheral PnP IDs enumeration support
  2013-04-04 18:23       ` Greg Kroah-Hartman
@ 2013-04-07  3:11         ` Zheng, Lv
  0 siblings, 0 replies; 71+ messages in thread
From: Zheng, Lv @ 2013-04-07  3:11 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Wysocki, Rafael J, Brown, Len, linux-acpi, linux-serial, Zhang, Rui

> > Hi,
> >
> > > > > +	return snprintf(buf, PAGE_SIZE, "uart\n");
> > > >
> > > > What is the "uart" part for?  Isn't that kind of obvious?
> > >
> > > It is the default "peripheral_type" content.
> > > Before sending next revision out, let me ask a question here:
> > > Shall I leave this empty as my previous revisions?
> >
> > Hmm, if this is empty by default then the udev rules may require additional
> handlings.
> > Can I use "NONE" here for the user space users?
> 
> No, just don't create the sysfs file at all for that type of device.

Sorry for the delayed reply.

I was thinking developers are encouraged to use the default attributes.
In the tty subsystem, the default attributes are owned by the tty drivers, thus I added peripheral_type file in the serial layer.

If we want to have it as a dynamic attribute, we may modify the codes in the tty_register_device_attr, splitting device_register into two parts, and add sysfs addition codes between the device_initialize and device_add.
So that the uevents received by the user space can have this attribute set.
Sorry that I need your confirmation before doing such modification.

Thanks and best regards
-Lv

^ permalink raw reply	[flat|nested] 71+ messages in thread

end of thread, other threads:[~2013-04-07  3:11 UTC | newest]

Thread overview: 71+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-12-03  3:39 [RFC PATCH 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
2012-12-03  3:39 ` [RFC PATCH 1/3] UART: Add UART subsystem as a bus Lv Zheng
2012-12-03 11:46   ` Alan Cox
2012-12-05  3:37     ` Zheng, Lv
2012-12-04 18:54   ` Mika Westerberg
2012-12-04 19:50     ` Alan Cox
2012-12-05  6:20       ` Mika Westerberg
2012-12-05  7:07         ` Zheng, Lv
2012-12-05  7:42           ` Mika Westerberg
2012-12-05  9:43         ` Alan Cox
2012-12-06  1:26           ` Zheng, Lv
2012-12-06  1:55             ` Zheng, Lv
2012-12-06 13:53             ` Alan Cox
2012-12-07  4:54               ` Zheng, Lv
2012-12-07  5:41                 ` Zheng, Lv
2012-12-07  7:24                   ` Huang Ying
2012-12-07 10:27                   ` Alan Cox
2012-12-07 10:25                 ` Alan Cox
2012-12-06  7:36           ` Zheng, Lv
2012-12-06  7:52             ` Mika Westerberg
2012-12-05  3:49     ` Zheng, Lv
2012-12-03  3:40 ` [RFC PATCH 2/3] ACPI / UART: Add ACPI enumeration support for UART bus Lv Zheng
2012-12-03  3:40 ` [RFC PATCH 3/3] UART: Add dummy devices to test the enumeration Lv Zheng
2012-12-05  3:51 ` [PATCH v2 0/4] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
2012-12-05  3:51   ` [PATCH v2 1/4] UART: Add UART subsystem as a bus Lv Zheng
2012-12-05  3:51   ` [PATCH v2 2/4] ACPI / UART: Add ACPI enumeration support for UART bus Lv Zheng
2012-12-05  3:51   ` [PATCH v2 3/4] UART / 8250: Add declearation of serial8250 driver Lv Zheng
2012-12-05  3:52   ` [PATCH v2 4/4] UART: Add dummy devices to test the enumeration Lv Zheng
2012-12-06  9:21 ` [RFC PATCH v3 0/4] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
2012-12-06  9:21   ` [RFC PATCH v3 1/4] UART: Add UART subsystem as a bus Lv Zheng
2012-12-06 13:40     ` Alan Cox
2012-12-07  3:52       ` Zheng, Lv
2012-12-07 10:22         ` Alan Cox
2012-12-06  9:22   ` [RFC PATCH v3 2/4] ACPI / UART: Add ACPI enumeration support for UART bus Lv Zheng
2012-12-06  9:22   ` [RFC PATCH v3 3/4] UART / 8250: Add declearation of serial8250 driver Lv Zheng
2012-12-06  9:22   ` [RFC PATCH v3 4/4] UART: Add dummy devices to test the enumeration Lv Zheng
2013-01-09  9:17 ` [RFC PATCH v4 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
2013-01-09  9:17   ` [RFC PATCH v4 1/3] TTY: Add TTY slave enumeration support Lv Zheng
2013-01-09  9:17   ` Lv Zheng
2013-01-09  9:18   ` [RFC PATCH v4 2/3] ACPI / UART: Add ACPI enumeration support for UART Lv Zheng
2013-01-09  9:18   ` Lv Zheng
2013-01-09  9:18   ` [RFC PATCH v4 3/3] UART: Add dummy devices to test the enumeration Lv Zheng
2013-01-09  9:18   ` Lv Zheng
2013-01-09  9:17 ` [RFC PATCH v4 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART Lv Zheng
2013-01-24 10:30 ` [PATCH v5 0/2] ACPI/UART: Add ACPI 5.0 enumeration " Lv Zheng
2013-01-24 10:30   ` [PATCH v5 1/2] TTY: Add TTY slave enumeration support Lv Zheng
2013-01-25 21:41     ` Greg Kroah-Hartman
2013-01-26  0:32       ` Alan Cox
2013-01-26  0:21         ` Greg Kroah-Hartman
2013-01-26  3:04       ` Zheng, Lv
2013-01-26  3:42         ` Greg Kroah-Hartman
2013-01-28  2:58           ` Zheng, Lv
2013-01-30  4:46             ` Greg Kroah-Hartman
2013-01-25 21:45     ` Greg Kroah-Hartman
2013-01-26  3:12       ` Zheng, Lv
2013-01-26  3:44         ` Greg Kroah-Hartman
2013-01-28  3:02           ` Zheng, Lv
2013-01-24 10:30   ` [PATCH v5 2/2] ACPI / UART: Add ACPI enumeration support for UART Lv Zheng
2013-01-24 10:30 ` [RFC PATCH v5] HACK: UART: Add dummy devices to test the enumeration Lv Zheng
2013-02-06  6:26 ` [RFC PATCH] ACPI / serial: Add UART change_pm support with ACPI power domain Lv Zheng
2013-02-06 19:07   ` Greg Kroah-Hartman
2013-02-06 21:39     ` Rafael J. Wysocki
2013-02-06 21:38       ` Greg Kroah-Hartman
2013-02-07  0:38         ` Zheng, Lv
2013-04-03  2:05 ` [PATCH v6] ACPI / serial: Add peripheral PnP IDs enumeration support Lv Zheng
2013-04-03 16:14   ` Greg Kroah-Hartman
2013-04-04 10:12     ` Zheng, Lv
2013-04-07  3:05       ` Zheng, Lv
2013-04-04 15:43     ` Zheng, Lv
2013-04-04 18:23       ` Greg Kroah-Hartman
2013-04-07  3:11         ` Zheng, Lv

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.