* [RFC PATCH v4 3/3] UART: Add dummy devices to test the enumeration.
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
In-Reply-To: <cover.1357837482.git.lv.zheng@intel.com>
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
* [RFC PATCH v4 3/3] UART: Add dummy devices to test the enumeration.
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
In-Reply-To: <cover.1357837482.git.lv.zheng@intel.com>
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
* [RFC PATCH v4 2/3] ACPI / UART: Add ACPI enumeration support for UART.
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
In-Reply-To: <cover.1357837482.git.lv.zheng@intel.com>
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
* [RFC PATCH v4 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART.
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-Reply-To: <cover.1354505471.git.lv.zheng@intel.com>
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
* [RFC PATCH v4 2/3] ACPI / UART: Add ACPI enumeration support for UART.
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
In-Reply-To: <cover.1357837482.git.lv.zheng@intel.com>
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
* [RFC PATCH v4 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART.
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-Reply-To: <cover.1354505471.git.lv.zheng@intel.com>
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
* [RFC PATCH v4 1/3] TTY: Add TTY slave enumeration support.
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-Reply-To: <cover.1357837482.git.lv.zheng@intel.com>
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
* [RFC PATCH v4 1/3] TTY: Add TTY slave enumeration support.
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-Reply-To: <cover.1357837482.git.lv.zheng@intel.com>
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
* Re: [PATCH 5/5] kfifo: log based kfifo API
From: Yuanhan Liu @ 2013-01-09 2:42 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: linux-kernel, Stefani Seibold, Andrew Morton, linux-omap,
linuxppc-dev, platform-driver-x86, linux-input, linux-iio,
linux-rdma, linux-media, linux-mmc, linux-mtd, libertas-dev,
linux-wireless, netdev, linux-pci, open-iscsi, linux-scsi, devel,
linux-serial, linux-usb, linux-mm, dccp, linux-sctp
In-Reply-To: <20130108181645.GA7972@core.coreip.homeip.net>
On Tue, Jan 08, 2013 at 10:16:46AM -0800, Dmitry Torokhov wrote:
> Hi Yuanhan,
>
> On Tue, Jan 08, 2013 at 10:57:53PM +0800, Yuanhan Liu wrote:
> > The current kfifo API take the kfifo size as input, while it rounds
> > _down_ the size to power of 2 at __kfifo_alloc. This may introduce
> > potential issue.
> >
> > Take the code at drivers/hid/hid-logitech-dj.c as example:
> >
> > if (kfifo_alloc(&djrcv_dev->notif_fifo,
> > DJ_MAX_NUMBER_NOTIFICATIONS * sizeof(struct dj_report),
> > GFP_KERNEL)) {
> >
> > Where, DJ_MAX_NUMBER_NOTIFICATIONS is 8, and sizeo of(struct dj_report)
> > is 15.
> >
> > Which means it wants to allocate a kfifo buffer which can store 8
> > dj_report entries at once. The expected kfifo buffer size would be
> > 8 * 15 = 120 then. While, in the end, __kfifo_alloc will turn the
> > size to rounddown_power_of_2(120) = 64, and then allocate a buf
> > with 64 bytes, which I don't think this is the original author want.
> >
> > With the new log API, we can do like following:
> >
> > int kfifo_size_order = order_base_2(DJ_MAX_NUMBER_NOTIFICATIONS *
> > sizeof(struct dj_report));
> >
> > if (kfifo_alloc(&djrcv_dev->notif_fifo, kfifo_size_order, GFP_KERNEL)) {
> >
> > This make sure we will allocate enough kfifo buffer for holding
> > DJ_MAX_NUMBER_NOTIFICATIONS dj_report entries.
>
> Why don't you simply change __kfifo_alloc to round the allocation up
> instead of down?
Hi Dmitry,
Yes, it would be neat and that was my first reaction as well. I then
sent out a patch, but it was NACKed by Stefani(the original kfifo
author). Here is the link:
https://lkml.org/lkml/2012/10/26/144
Then Stefani proposed to change the API to take log of size as input to
root fix this kind of issues. And here it is.
Thanks.
--yliu
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [PATCH V4 RESEND] serial: tegra: add serial driver
From: Stephen Warren @ 2013-01-08 22:23 UTC (permalink / raw)
To: Laxman Dewangan
Cc: alan-VuQAYsv1563Yd54FQh9/CA,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r, jslaby-AlSwsSmVLrQ,
grant.likely-s3s/WqlpOiPyB63q8FvJNQ,
rob.herring-bsGFqQB8/DxBDgjK7y7TUQ,
devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-serial-u79uwXL29TY76Z2rM5mHXA,
linux-tegra-u79uwXL29TY76Z2rM5mHXA, wmb-D5eQfiDGL7eakBO8gow8eQ
In-Reply-To: <1357642664-10816-1-git-send-email-ldewangan-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
On 01/08/2013 03:57 AM, Laxman Dewangan wrote:
> NVIDIA's Tegra has multiple UART controller which supports:
> - APB DMA based controller fifo read/write.
> - End Of Data interrupt in incoming data to know whether end
> of frame achieve or not.
> - HW controlled RTS and CTS flow control to reduce SW overhead.
>
> Add serial driver to use all above feature.
>
> Signed-off-by: Laxman Dewangan <ldewangan-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> Acked-by: Alan Cox <alan-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
The DT binding part of this patch,
Reviewed-by: Stephen Warren <swarren-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
^ permalink raw reply
* Bluetooth / TTY: [ 1806.484970] INFO: task kworker/0:1:25023 blocked for more than 120 seconds.
From: Sander Eikelenboom @ 2013-01-08 22:00 UTC (permalink / raw)
To: linux-kernel, linux-bluetooth, linux-serial
Cc: marcel, Greg Kroah-Hartman, Alan Cox
I'm trying to use a USB bluetooth dongle to connect to a bluetooth to serial device with RFCOMM.
It's able to work fine for some time, but tt consistently fails after some time.
This is sometimes right on the start when connecting to the /dev/rfcomm0, but it can also require several hours of running fine while connected and exchanging data.
This is the stacktrace i get:
[ 1806.484970] INFO: task kworker/0:1:25023 blocked for more than 120 seconds.
[ 1806.503488] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
[ 1806.521864] kworker/0:1 D 0000000000000201 0 25023 2 0x00000000
[ 1806.540026] ffff88000baa7be8 0000000000000216 ffff880037079148 ffff880037079148
[ 1806.557926] ffff8800386fa0e0 0000000000013040 ffff88000baa7fd8 ffff88000baa6010
[ 1806.575622] 0000000000013040 0000000000013040 ffff88000baa7fd8 0000000000013040
[ 1806.592981] Call Trace:
[ 1806.610066] [<ffffffff810b5bd7>] ? lock_release+0x117/0x250
[ 1806.627150] [<ffffffff810b5748>] ? lock_acquire+0xd8/0x100
[ 1806.643901] [<ffffffff819ba2fe>] ? tty_lock_nested+0x3e/0x80
[ 1806.660460] [<ffffffff819b8a14>] schedule+0x24/0x70
[ 1806.676724] [<ffffffff819b8ef3>] schedule_preempt_disabled+0x13/0x20
[ 1806.692780] [<ffffffff819b73bb>] mutex_lock_nested+0x1ab/0x450
[ 1806.708582] [<ffffffff819ba2fe>] ? tty_lock_nested+0x3e/0x80
[ 1806.724140] [<ffffffff819ba2fe>] tty_lock_nested+0x3e/0x80
[ 1806.739421] [<ffffffff819ba34b>] tty_lock+0xb/0x10
[ 1806.754418] [<ffffffff81449495>] __tty_hangup+0x65/0x3c0
[ 1806.769153] [<ffffffff81080bf8>] ? process_one_work+0x158/0x4b0
[ 1806.783648] [<ffffffff81449800>] do_tty_hangup+0x10/0x20
[ 1806.797905] [<ffffffff81080c60>] process_one_work+0x1c0/0x4b0
[ 1806.811958] [<ffffffff81080bf8>] ? process_one_work+0x158/0x4b0
[ 1806.825752] [<ffffffff814497f0>] ? __tty_hangup+0x3c0/0x3c0
[ 1806.839332] [<ffffffff8108134e>] worker_thread+0x11e/0x3d0
[ 1806.852654] [<ffffffff81081230>] ? manage_workers+0x2e0/0x2e0
[ 1806.865719] [<ffffffff81088a36>] kthread+0xd6/0xe0
[ 1806.878518] [<ffffffff81088960>] ? __init_kthread_worker+0x70/0x70
[ 1806.891064] [<ffffffff819baebc>] ret_from_fork+0x7c/0xb0
[ 1806.903376] [<ffffffff81088960>] ? __init_kthread_worker+0x70/0x70
[ 1806.939888] INFO: lockdep is turned off.
[ 1806.951766] INFO: task zabbix_slimmeme:27798 blocked for more than 120 seconds.
[ 1806.963521] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
[ 1806.975059] zabbix_slimmeme D ffff88002a619070 0 27798 27355 0x00000000
[ 1806.986497] ffff880000bb7818 0000000000000216 ffff880000000002 ffffffff8202ae38
[ 1806.997893] ffff88002a619070 0000000000013040 ffff880000bb7fd8 ffff880000bb6010
[ 1807.008944] 0000000000013040 0000000000013040 ffff880000bb7fd8 0000000000013040
[ 1807.019692] Call Trace:
[ 1807.030165] [<ffffffff810be1ad>] ? __module_text_address+0xd/0x60
[ 1807.040524] [<ffffffff810be1ad>] ? __module_text_address+0xd/0x60
[ 1807.050568] [<ffffffff810be40b>] ? is_module_text_address+0x2b/0x60
[ 1807.060389] [<ffffffff81085958>] ? __kernel_text_address+0x58/0x80
[ 1807.069996] [<ffffffff81070087>] ? local_bh_disable+0x17/0x20
[ 1807.079383] [<ffffffff810b5748>] ? lock_acquire+0xd8/0x100
[ 1807.088467] [<ffffffff819b8a14>] schedule+0x24/0x70
[ 1807.097296] [<ffffffff819b5c7d>] schedule_timeout+0x1bd/0x220
[ 1807.105884] [<ffffffff810b5748>] ? lock_acquire+0xd8/0x100
[ 1807.114211] [<ffffffff819b7f11>] ? wait_for_common+0x31/0x170
[ 1807.122301] [<ffffffff810b5bd7>] ? lock_release+0x117/0x250
[ 1807.130156] [<ffffffff819b7fe1>] wait_for_common+0x101/0x170
[ 1807.137804] [<ffffffff810986f0>] ? try_to_wake_up+0x310/0x310
[ 1807.145193] [<ffffffff819b80f8>] wait_for_completion+0x18/0x20
[ 1807.152350] [<ffffffff81083385>] flush_work+0x195/0x250
[ 1807.159275] [<ffffffff810833a0>] ? flush_work+0x1b0/0x250
[ 1807.165957] [<ffffffff81080400>] ? cwq_dec_nr_in_flight+0xd0/0xd0
[ 1807.172401] [<ffffffff81451748>] tty_ldisc_flush_works+0x18/0x40
[ 1807.178634] [<ffffffff8145198e>] tty_ldisc_release+0x2e/0x90
[ 1807.184586] [<ffffffff8144ba07>] tty_release+0x3c7/0x590
[ 1807.190264] [<ffffffff810b19ed>] ? trace_hardirqs_on+0xd/0x10
[ 1807.195910] [<ffffffff819b60b9>] ? __mutex_unlock_slowpath+0x149/0x1d0
[ 1807.201455] [<ffffffff810986f0>] ? try_to_wake_up+0x310/0x310
[ 1807.206927] [<ffffffff8144bf94>] tty_open+0x3c4/0x5f0
[ 1807.212366] [<ffffffff81150c88>] chrdev_open+0x98/0x170
[ 1807.217803] [<ffffffff8109128d>] ? lg_local_unlock+0x3d/0x70
[ 1807.223255] [<ffffffff81150bf0>] ? cdev_put+0x30/0x30
[ 1807.228678] [<ffffffff8114b46e>] do_dentry_open+0x25e/0x310
[ 1807.234040] [<ffffffff8114b630>] finish_open+0x30/0x50
[ 1807.239445] [<ffffffff8115aa0e>] do_last+0x30e/0xe90
[ 1807.244805] [<ffffffff81157d2a>] ? link_path_walk+0x9a/0x9f0
[ 1807.250170] [<ffffffff8115b63e>] path_openat+0xae/0x4e0
[ 1807.255503] [<ffffffff810b5bd7>] ? lock_release+0x117/0x250
[ 1807.260835] [<ffffffff811602d4>] ? do_select+0x3f4/0x6d0
[ 1807.266174] [<ffffffff8115bba4>] do_filp_open+0x44/0xa0
[ 1807.271504] [<ffffffff81169453>] ? __alloc_fd+0xb3/0x150
[ 1807.276904] [<ffffffff8114af83>] do_sys_open+0x103/0x1f0
[ 1807.282262] [<ffffffff8114b0ac>] sys_open+0x1c/0x20
[ 1807.287579] [<ffffffff819baf69>] system_call_fastpath+0x16/0x1b
[ 1807.292892] INFO: lockdep is turned off.
^ permalink raw reply
* [PATCH 6/7] tty: of_serial: Add pinctrl support
From: Maxime Ripard @ 2013-01-08 21:43 UTC (permalink / raw)
To: Arnd Bergmann, Olof Johansson
Cc: linux-arm-kernel, Stefan Roese, Alejandro Mery, Alan Cox,
Greg Kroah-Hartman, Jiri Slaby, linux-serial, linux-kernel
In-Reply-To: <1357681398-23956-1-git-send-email-maxime.ripard@free-electrons.com>
Use pinctrl to configure the SoCs pins directly from the driver.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
drivers/tty/serial/of_serial.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c
index e7cae1c..e9f3289 100644
--- a/drivers/tty/serial/of_serial.c
+++ b/drivers/tty/serial/of_serial.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/serial_core.h>
#include <linux/serial_8250.h>
#include <linux/serial_reg.h>
@@ -57,6 +58,7 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
struct of_serial_info *info)
{
struct resource resource;
+ struct pinctrl *pinctrl;
struct device_node *np = ofdev->dev.of_node;
u32 clk, spd, prop;
int ret;
@@ -85,6 +87,11 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
goto out;
}
+ pinctrl = devm_pinctrl_get_select_default(&ofdev->dev);
+ if (IS_ERR(pinctrl))
+ dev_warn(&ofdev->dev,
+ "pins are not configured from the driver\n");
+
spin_lock_init(&port->lock);
port->mapbase = resource.start;
--
1.7.10.4
^ permalink raw reply related
* Re: [PATCH 5/5] kfifo: log based kfifo API
From: Andy Walls @ 2013-01-08 21:10 UTC (permalink / raw)
To: Dmitry Torokhov, Yuanhan Liu
Cc: linux-kernel, Stefani Seibold, Andrew Morton, linux-omap,
linuxppc-dev, platform-driver-x86, linux-input, linux-iio,
linux-rdma, linux-media, linux-mmc, linux-mtd, libertas-dev,
linux-wireless, netdev, linux-pci, open-iscsi, linux-scsi, devel,
linux-serial, linux-usb, linux-mm, dccp, linux-sctp
In-Reply-To: <20130108181645.GA7972@core.coreip.homeip.net>
Dmitry Torokhov <dmitry.torokhov@gmail.com> wrote:
>Hi Yuanhan,
>
>On Tue, Jan 08, 2013 at 10:57:53PM +0800, Yuanhan Liu wrote:
>> The current kfifo API take the kfifo size as input, while it rounds
>> _down_ the size to power of 2 at __kfifo_alloc. This may introduce
>> potential issue.
>>
>> Take the code at drivers/hid/hid-logitech-dj.c as example:
>>
>> if (kfifo_alloc(&djrcv_dev->notif_fifo,
>> DJ_MAX_NUMBER_NOTIFICATIONS * sizeof(struct
>dj_report),
>> GFP_KERNEL)) {
>>
>> Where, DJ_MAX_NUMBER_NOTIFICATIONS is 8, and sizeo of(struct
>dj_report)
>> is 15.
>>
>> Which means it wants to allocate a kfifo buffer which can store 8
>> dj_report entries at once. The expected kfifo buffer size would be
>> 8 * 15 = 120 then. While, in the end, __kfifo_alloc will turn the
>> size to rounddown_power_of_2(120) = 64, and then allocate a buf
>> with 64 bytes, which I don't think this is the original author want.
>>
>> With the new log API, we can do like following:
>>
>> int kfifo_size_order = order_base_2(DJ_MAX_NUMBER_NOTIFICATIONS *
>> sizeof(struct dj_report));
>>
>> if (kfifo_alloc(&djrcv_dev->notif_fifo, kfifo_size_order,
>GFP_KERNEL)) {
>>
>> This make sure we will allocate enough kfifo buffer for holding
>> DJ_MAX_NUMBER_NOTIFICATIONS dj_report entries.
>
>Why don't you simply change __kfifo_alloc to round the allocation up
>instead of down?
>
>Thanks.
>
>--
>Dmitry
>--
>To unsubscribe from this list: send the line "unsubscribe linux-media"
>in
>the body of a message to majordomo@vger.kernel.org
>More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Dmitry,
I agree. I don't see the benefit in pushing up the change to a kfifo internal decision/problem to many different places in the kernel.
Regards,
Andy
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [PATCH 5/5] kfifo: log based kfifo API
From: Dmitry Torokhov @ 2013-01-08 18:16 UTC (permalink / raw)
To: Yuanhan Liu
Cc: linux-kernel, Stefani Seibold, Andrew Morton, linux-omap,
linuxppc-dev, platform-driver-x86, linux-input, linux-iio,
linux-rdma, linux-media, linux-mmc, linux-mtd, libertas-dev,
linux-wireless, netdev, linux-pci, open-iscsi, linux-scsi, devel,
linux-serial, linux-usb, linux-mm, dccp, linux-sctp
In-Reply-To: <1357657073-27352-6-git-send-email-yuanhan.liu@linux.intel.com>
Hi Yuanhan,
On Tue, Jan 08, 2013 at 10:57:53PM +0800, Yuanhan Liu wrote:
> The current kfifo API take the kfifo size as input, while it rounds
> _down_ the size to power of 2 at __kfifo_alloc. This may introduce
> potential issue.
>
> Take the code at drivers/hid/hid-logitech-dj.c as example:
>
> if (kfifo_alloc(&djrcv_dev->notif_fifo,
> DJ_MAX_NUMBER_NOTIFICATIONS * sizeof(struct dj_report),
> GFP_KERNEL)) {
>
> Where, DJ_MAX_NUMBER_NOTIFICATIONS is 8, and sizeo of(struct dj_report)
> is 15.
>
> Which means it wants to allocate a kfifo buffer which can store 8
> dj_report entries at once. The expected kfifo buffer size would be
> 8 * 15 = 120 then. While, in the end, __kfifo_alloc will turn the
> size to rounddown_power_of_2(120) = 64, and then allocate a buf
> with 64 bytes, which I don't think this is the original author want.
>
> With the new log API, we can do like following:
>
> int kfifo_size_order = order_base_2(DJ_MAX_NUMBER_NOTIFICATIONS *
> sizeof(struct dj_report));
>
> if (kfifo_alloc(&djrcv_dev->notif_fifo, kfifo_size_order, GFP_KERNEL)) {
>
> This make sure we will allocate enough kfifo buffer for holding
> DJ_MAX_NUMBER_NOTIFICATIONS dj_report entries.
Why don't you simply change __kfifo_alloc to round the allocation up
instead of down?
Thanks.
--
Dmitry
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* [PATCH 5/5] kfifo: log based kfifo API
From: Yuanhan Liu @ 2013-01-08 14:57 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: Yuanhan Liu, Stefani Seibold, Andrew Morton,
linux-omap-u79uwXL29TY76Z2rM5mHXA,
linuxppc-dev-uLR06cmDAlY/bJ5BZ2RsiQ,
platform-driver-x86-u79uwXL29TY76Z2rM5mHXA,
linux-input-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-rdma-u79uwXL29TY76Z2rM5mHXA,
linux-media-u79uwXL29TY76Z2rM5mHXA,
linux-mmc-u79uwXL29TY76Z2rM5mHXA,
linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
libertas-dev-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-wireless-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA, linux-pci-u79uwXL29TY76Z2rM5mHXA,
open-iscsi-/JYPxA39Uh5TLH3MbocFFw,
linux-scsi-u79uwXL29TY76Z2rM5mHXA,
devel-gWbeCf7V1WCQmaza687I9mD2FQJk+8+b,
linux-serial-u79uwXL29TY76Z2rM5mHXA,
linux-usb-u79uwXL29TY76Z2rM5mHXA, linux-mm-Bw31MaZKKs3YtjvyW6yDsg,
dccp-u79uwXL29TY76Z2rM5mHXA, linux-sctp-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1357657073-27352-1-git-send-email-yuanhan.liu-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
The current kfifo API take the kfifo size as input, while it rounds
_down_ the size to power of 2 at __kfifo_alloc. This may introduce
potential issue.
Take the code at drivers/hid/hid-logitech-dj.c as example:
if (kfifo_alloc(&djrcv_dev->notif_fifo,
DJ_MAX_NUMBER_NOTIFICATIONS * sizeof(struct dj_report),
GFP_KERNEL)) {
Where, DJ_MAX_NUMBER_NOTIFICATIONS is 8, and sizeo of(struct dj_report)
is 15.
Which means it wants to allocate a kfifo buffer which can store 8
dj_report entries at once. The expected kfifo buffer size would be
8 * 15 = 120 then. While, in the end, __kfifo_alloc will turn the
size to rounddown_power_of_2(120) = 64, and then allocate a buf
with 64 bytes, which I don't think this is the original author want.
With the new log API, we can do like following:
int kfifo_size_order = order_base_2(DJ_MAX_NUMBER_NOTIFICATIONS *
sizeof(struct dj_report));
if (kfifo_alloc(&djrcv_dev->notif_fifo, kfifo_size_order, GFP_KERNEL)) {
This make sure we will allocate enough kfifo buffer for holding
DJ_MAX_NUMBER_NOTIFICATIONS dj_report entries.
Cc: Stefani Seibold <stefani-mkwtCZVSLSnR7s880joybQ@public.gmane.org>
Cc: Andrew Morton <akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b@public.gmane.org>
Cc: linux-omap-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linuxppc-dev-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org
Cc: platform-driver-x86-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-input-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-rdma-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-media-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-mmc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
Cc: libertas-dev-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-pci-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: open-iscsi-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
Cc: linux-scsi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: devel-gWbeCf7V1WCQmaza687I9mD2FQJk+8+b@public.gmane.org
Cc: linux-serial-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-usb-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-mm-Bw31MaZKKs3YtjvyW6yDsg@public.gmane.org
Cc: dccp-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-sctp-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Signed-off-by: Yuanhan Liu <yuanhan.liu-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
---
arch/arm/plat-omap/Kconfig | 2 +-
arch/arm/plat-omap/mailbox.c | 6 +++-
arch/powerpc/sysdev/fsl_rmu.c | 2 +-
drivers/char/sonypi.c | 9 ++++---
drivers/hid/hid-logitech-dj.c | 7 +++--
drivers/iio/industrialio-event.c | 2 +-
drivers/iio/kfifo_buf.c | 3 +-
drivers/infiniband/hw/cxgb3/cxio_resource.c | 8 ++++--
drivers/media/i2c/cx25840/cx25840-ir.c | 9 +++++--
drivers/media/pci/cx23885/cx23888-ir.c | 9 +++++--
drivers/media/pci/meye/meye.c | 6 +---
drivers/media/pci/meye/meye.h | 2 +
drivers/media/rc/ir-raw.c | 7 +++--
drivers/memstick/host/r592.h | 2 +-
drivers/mmc/card/sdio_uart.c | 4 ++-
drivers/mtd/sm_ftl.c | 5 +++-
drivers/net/wireless/libertas/main.c | 4 ++-
drivers/net/wireless/rt2x00/rt2x00dev.c | 5 +--
drivers/pci/pcie/aer/aerdrv_core.c | 3 +-
drivers/platform/x86/fujitsu-laptop.c | 5 ++-
drivers/platform/x86/sony-laptop.c | 6 ++--
drivers/rapidio/devices/tsi721.c | 5 ++-
drivers/scsi/libiscsi_tcp.c | 6 +++-
drivers/staging/omapdrm/omap_plane.c | 5 +++-
drivers/tty/n_gsm.c | 4 ++-
drivers/tty/nozomi.c | 5 +--
drivers/tty/serial/ifx6x60.c | 2 +-
drivers/tty/serial/ifx6x60.h | 3 +-
drivers/tty/serial/kgdb_nmi.c | 7 +++--
drivers/usb/host/fhci.h | 4 ++-
drivers/usb/serial/cypress_m8.c | 4 +-
drivers/usb/serial/io_ti.c | 4 +-
drivers/usb/serial/ti_usb_3410_5052.c | 7 +++--
drivers/usb/serial/usb-serial.c | 2 +-
include/linux/kfifo.h | 31 +++++++++++++--------------
include/linux/rio.h | 1 +
include/media/lirc_dev.h | 4 ++-
kernel/kfifo.c | 9 +------
mm/memory-failure.c | 3 +-
net/dccp/probe.c | 6 +++-
net/sctp/probe.c | 6 +++-
samples/kfifo/bytestream-example.c | 8 +++---
samples/kfifo/dma-example.c | 5 ++-
samples/kfifo/inttype-example.c | 7 +++--
samples/kfifo/record-example.c | 6 ++--
45 files changed, 142 insertions(+), 108 deletions(-)
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig
index 665870d..7eda02c 100644
--- a/arch/arm/plat-omap/Kconfig
+++ b/arch/arm/plat-omap/Kconfig
@@ -124,7 +124,7 @@ config OMAP_MBOX_FWK
DSP, IVA1.0 and IVA2 in OMAP1/2/3.
config OMAP_MBOX_KFIFO_SIZE
- int "Mailbox kfifo default buffer size (bytes)"
+ int "Mailbox kfifo default buffer size (bytes, should be power of 2. If not, will roundup to power of 2"
depends on OMAP_MBOX_FWK
default 256
help
diff --git a/arch/arm/plat-omap/mailbox.c b/arch/arm/plat-omap/mailbox.c
index 42377ef..848fa0b 100644
--- a/arch/arm/plat-omap/mailbox.c
+++ b/arch/arm/plat-omap/mailbox.c
@@ -30,6 +30,7 @@
#include <linux/err.h>
#include <linux/notifier.h>
#include <linux/module.h>
+#include <linux/log2.h>
#include <plat/mailbox.h>
@@ -40,7 +41,7 @@ static DEFINE_MUTEX(mbox_configured_lock);
static unsigned int mbox_kfifo_size = CONFIG_OMAP_MBOX_KFIFO_SIZE;
module_param(mbox_kfifo_size, uint, S_IRUGO);
-MODULE_PARM_DESC(mbox_kfifo_size, "Size of omap's mailbox kfifo (bytes)");
+MODULE_PARM_DESC(mbox_kfifo_size, "Size of omap's mailbox kfifo (bytes, should be power of 2. If not, will roundup to power of 2)");
/* Mailbox FIFO handle functions */
static inline mbox_msg_t mbox_fifo_read(struct omap_mbox *mbox)
@@ -218,6 +219,7 @@ static struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox,
void (*tasklet)(unsigned long))
{
struct omap_mbox_queue *mq;
+ int mbox_kfifo_size_order = order_base_2(mbox_kfifo_size);
mq = kzalloc(sizeof(struct omap_mbox_queue), GFP_KERNEL);
if (!mq)
@@ -225,7 +227,7 @@ static struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox,
spin_lock_init(&mq->lock);
- if (kfifo_alloc(&mq->fifo, mbox_kfifo_size, GFP_KERNEL))
+ if (kfifo_alloc(&mq->fifo, mbox_kfifo_size_order, GFP_KERNEL))
goto error;
if (work)
diff --git a/arch/powerpc/sysdev/fsl_rmu.c b/arch/powerpc/sysdev/fsl_rmu.c
index 14bd522..84d2b8c 100644
--- a/arch/powerpc/sysdev/fsl_rmu.c
+++ b/arch/powerpc/sysdev/fsl_rmu.c
@@ -587,7 +587,7 @@ int fsl_rio_port_write_init(struct fsl_rio_pw *pw)
INIT_WORK(&pw->pw_work, fsl_pw_dpc);
spin_lock_init(&pw->pw_fifo_lock);
- if (kfifo_alloc(&pw->pw_fifo, RIO_PW_MSG_SIZE * 32, GFP_KERNEL)) {
+ if (kfifo_alloc(&pw->pw_fifo, RIO_KFIFO_SIZE_ORDER, GFP_KERNEL)) {
pr_err("FIFO allocation failed\n");
rc = -ENOMEM;
goto err_out_irq;
diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c
index d780295..39d8dd7 100644
--- a/drivers/char/sonypi.c
+++ b/drivers/char/sonypi.c
@@ -429,7 +429,7 @@ static struct sonypi_eventtypes {
{ 0 }
};
-#define SONYPI_BUF_SIZE 128
+#define SONYPI_KFIFO_SIZE_ORDER 7
/* Correspondance table between sonypi events and input layer events */
static struct {
@@ -1316,7 +1316,8 @@ static int sonypi_probe(struct platform_device *dev)
"http://www.linux.it/~malattia/wiki/index.php/Sony_drivers\n");
spin_lock_init(&sonypi_device.fifo_lock);
- error = kfifo_alloc(&sonypi_device.fifo, SONYPI_BUF_SIZE, GFP_KERNEL);
+ error = kfifo_alloc(&sonypi_device.fifo, SONYPI_KFIFO_SIZE_ORDER,
+ GFP_KERNEL);
if (error) {
printk(KERN_ERR "sonypi: kfifo_alloc failed\n");
return error;
@@ -1395,8 +1396,8 @@ static int sonypi_probe(struct platform_device *dev)
}
spin_lock_init(&sonypi_device.input_fifo_lock);
- error = kfifo_alloc(&sonypi_device.input_fifo, SONYPI_BUF_SIZE,
- GFP_KERNEL);
+ error = kfifo_alloc(&sonypi_device.input_fifo,
+ SONYPI_KFIFO_SIZE_ORDER, GFP_KERNEL);
if (error) {
printk(KERN_ERR "sonypi: kfifo_alloc failed\n");
goto err_inpdev_unregister;
diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
index 9500f2f..031be77 100644
--- a/drivers/hid/hid-logitech-dj.c
+++ b/drivers/hid/hid-logitech-dj.c
@@ -26,6 +26,7 @@
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/usb.h>
+#include <linux/log2.h>
#include <asm/unaligned.h>
#include "usbhid/usbhid.h"
#include "hid-ids.h"
@@ -730,6 +731,8 @@ static int logi_dj_probe(struct hid_device *hdev,
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
struct dj_receiver_dev *djrcv_dev;
int retval;
+ int kfifo_size_order = order_base_2(DJ_MAX_NUMBER_NOTIFICATIONS *
+ sizeof(struct dj_report));
if (is_dj_device((struct dj_device *)hdev->driver_data))
return -ENODEV;
@@ -757,9 +760,7 @@ static int logi_dj_probe(struct hid_device *hdev,
djrcv_dev->hdev = hdev;
INIT_WORK(&djrcv_dev->work, delayedwork_callback);
spin_lock_init(&djrcv_dev->lock);
- if (kfifo_alloc(&djrcv_dev->notif_fifo,
- DJ_MAX_NUMBER_NOTIFICATIONS * sizeof(struct dj_report),
- GFP_KERNEL)) {
+ if (kfifo_alloc(&djrcv_dev->notif_fifo, kfifo_size_order, GFP_KERNEL)) {
dev_err(&hdev->dev,
"%s:failed allocating notif_fifo\n", __func__);
kfree(djrcv_dev);
diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c
index 261cae0..9b73680 100644
--- a/drivers/iio/industrialio-event.c
+++ b/drivers/iio/industrialio-event.c
@@ -35,7 +35,7 @@
*/
struct iio_event_interface {
wait_queue_head_t wait;
- DECLARE_KFIFO(det_events, struct iio_event_data, 16);
+ DECLARE_KFIFO(det_events, struct iio_event_data, 4);
struct list_head dev_attr_list;
unsigned long flags;
diff --git a/drivers/iio/kfifo_buf.c b/drivers/iio/kfifo_buf.c
index 5bc5c86..d8ba52ff 100644
--- a/drivers/iio/kfifo_buf.c
+++ b/drivers/iio/kfifo_buf.c
@@ -7,6 +7,7 @@
#include <linux/mutex.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/sched.h>
+#include <linux/log2.h>
struct iio_kfifo {
struct iio_buffer buffer;
@@ -23,7 +24,7 @@ static inline int __iio_allocate_kfifo(struct iio_kfifo *buf,
return -EINVAL;
__iio_update_buffer(&buf->buffer, bytes_per_datum, length);
- return __kfifo_alloc((struct __kfifo *)&buf->kf, length,
+ return __kfifo_alloc((struct __kfifo *)&buf->kf, order_base_2(length),
bytes_per_datum, GFP_KERNEL);
}
diff --git a/drivers/infiniband/hw/cxgb3/cxio_resource.c b/drivers/infiniband/hw/cxgb3/cxio_resource.c
index 31f9201..186d05e 100644
--- a/drivers/infiniband/hw/cxgb3/cxio_resource.c
+++ b/drivers/infiniband/hw/cxgb3/cxio_resource.c
@@ -36,6 +36,7 @@
#include <linux/kfifo.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
+#include <linux/log2.h>
#include "cxio_resource.h"
#include "cxio_hal.h"
@@ -54,8 +55,9 @@ static int __cxio_init_resource_fifo(struct kfifo *fifo,
u32 random_bytes;
u32 rarray[16];
spin_lock_init(fifo_lock);
+ int kfifo_size_order = order_base_2(nr * sizeof(u32));
- if (kfifo_alloc(fifo, nr * sizeof(u32), GFP_KERNEL))
+ if (kfifo_alloc(fifo, kfifo_size_order, GFP_KERNEL))
return -ENOMEM;
for (i = 0; i < skip_low + skip_high; i++)
@@ -111,11 +113,11 @@ static int cxio_init_resource_fifo_random(struct kfifo *fifo,
static int cxio_init_qpid_fifo(struct cxio_rdev *rdev_p)
{
u32 i;
+ int kfifo_size_order = order_base_2(T3_MAX_NUM_QP * sizeof(u32));
spin_lock_init(&rdev_p->rscp->qpid_fifo_lock);
- if (kfifo_alloc(&rdev_p->rscp->qpid_fifo, T3_MAX_NUM_QP * sizeof(u32),
- GFP_KERNEL))
+ if (kfifo_alloc(&rdev_p->rscp->qpid_fifo, kfifo_size_order, GFP_KERNEL))
return -ENOMEM;
for (i = 16; i < T3_MAX_NUM_QP; i++)
diff --git a/drivers/media/i2c/cx25840/cx25840-ir.c b/drivers/media/i2c/cx25840/cx25840-ir.c
index 38ce76e..1da0b6c 100644
--- a/drivers/media/i2c/cx25840/cx25840-ir.c
+++ b/drivers/media/i2c/cx25840/cx25840-ir.c
@@ -24,6 +24,7 @@
#include <linux/slab.h>
#include <linux/kfifo.h>
#include <linux/module.h>
+#include <linux/log2.h>
#include <media/cx25840.h>
#include <media/rc-core.h>
@@ -106,8 +107,10 @@ union cx25840_ir_fifo_rec {
struct ir_raw_event ir_core_data;
};
-#define CX25840_IR_RX_KFIFO_SIZE (256 * sizeof(union cx25840_ir_fifo_rec))
-#define CX25840_IR_TX_KFIFO_SIZE (256 * sizeof(union cx25840_ir_fifo_rec))
+#define CX25840_IR_RX_KFIFO_SIZE_ORDER (order_base_2(256 * sizeof(union cx25840_ir_fifo_rec)))
+#define CX25840_IR_RX_KFIFO_SIZE (1<<CX25840_IR_RX_KFIFO_SIZE_ORDER)
+#define CX25840_IR_TX_KFIFO_SIZE_ORDER (order_base_2(256 * sizeof(union cx25840_ir_fifo_rec)))
+#define CX25840_IR_TX_KFIFO_SIZE (CX25840_IR_TX_KFIFO_SIZE_ORDER)
struct cx25840_ir_state {
struct i2c_client *c;
@@ -1236,7 +1239,7 @@ int cx25840_ir_probe(struct v4l2_subdev *sd)
spin_lock_init(&ir_state->rx_kfifo_lock);
if (kfifo_alloc(&ir_state->rx_kfifo,
- CX25840_IR_RX_KFIFO_SIZE, GFP_KERNEL)) {
+ CX25840_IR_RX_KFIFO_SIZE_ORDER, GFP_KERNEL)) {
kfree(ir_state);
return -ENOMEM;
}
diff --git a/drivers/media/pci/cx23885/cx23888-ir.c b/drivers/media/pci/cx23885/cx23888-ir.c
index c4bd1e9..4c6e24b 100644
--- a/drivers/media/pci/cx23885/cx23888-ir.c
+++ b/drivers/media/pci/cx23885/cx23888-ir.c
@@ -23,6 +23,7 @@
#include <linux/kfifo.h>
#include <linux/slab.h>
+#include <linux/log2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
@@ -125,8 +126,10 @@ union cx23888_ir_fifo_rec {
struct ir_raw_event ir_core_data;
};
-#define CX23888_IR_RX_KFIFO_SIZE (256 * sizeof(union cx23888_ir_fifo_rec))
-#define CX23888_IR_TX_KFIFO_SIZE (256 * sizeof(union cx23888_ir_fifo_rec))
+#define CX23888_IR_RX_KFIFO_SIZE_ORDER (order_base_2(256 * sizeof(union cx23888_ir_fifo_rec)))
+#define CX23888_IR_RX_KFIFO_SIZE (1<<CX23888_IR_RX_KFIFO_SIZE_ORDER)
+#define CX23888_IR_TX_KFIFO_SIZE_ORDER (order_base_2(256 * sizeof(union cx23888_ir_fifo_rec)))
+#define CX23888_IR_TX_KFIFO_SIZE (1<<CX23888_IR_TX_KFIFO_SIZE_ORDER)
struct cx23888_ir_state {
struct v4l2_subdev sd;
@@ -1213,7 +1216,7 @@ int cx23888_ir_probe(struct cx23885_dev *dev)
return -ENOMEM;
spin_lock_init(&state->rx_kfifo_lock);
- if (kfifo_alloc(&state->rx_kfifo, CX23888_IR_RX_KFIFO_SIZE, GFP_KERNEL))
+ if (kfifo_alloc(&state->rx_kfifo, CX23888_IR_RX_KFIFO_SIZE_ORDER, GFP_KERNEL))
return -ENOMEM;
state->dev = dev;
diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c
index 049e186..3bcde0c 100644
--- a/drivers/media/pci/meye/meye.c
+++ b/drivers/media/pci/meye/meye.c
@@ -1759,14 +1759,12 @@ static int meye_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
}
spin_lock_init(&meye.grabq_lock);
- if (kfifo_alloc(&meye.grabq, sizeof(int) * MEYE_MAX_BUFNBRS,
- GFP_KERNEL)) {
+ if (kfifo_alloc(&meye.grabq, MEYE_KFIFO_SIZE_ORDER, GFP_KERNEL)) {
v4l2_err(v4l2_dev, "fifo allocation failed\n");
goto outkfifoalloc1;
}
spin_lock_init(&meye.doneq_lock);
- if (kfifo_alloc(&meye.doneq, sizeof(int) * MEYE_MAX_BUFNBRS,
- GFP_KERNEL)) {
+ if (kfifo_alloc(&meye.doneq, MEYE_KFIFO_SIZE_ORDER, GFP_KERNEL)) {
v4l2_err(v4l2_dev, "fifo allocation failed\n");
goto outkfifoalloc2;
}
diff --git a/drivers/media/pci/meye/meye.h b/drivers/media/pci/meye/meye.h
index 4bdeb03..5d3ab4f 100644
--- a/drivers/media/pci/meye/meye.h
+++ b/drivers/media/pci/meye/meye.h
@@ -260,6 +260,7 @@
/* private API definitions */
#include <linux/meye.h>
#include <linux/mutex.h>
+#include <linux/log2.h>
/* Enable jpg software correction */
@@ -270,6 +271,7 @@
/* Maximum number of buffers */
#define MEYE_MAX_BUFNBRS 32
+#define MEYE_KFIFO_SIZE_ORDER (order_base_2(MEYE_MAX_BUFNBRS * sizeof(int)))
/* State of a buffer */
#define MEYE_BUF_UNUSED 0 /* not used */
diff --git a/drivers/media/rc/ir-raw.c b/drivers/media/rc/ir-raw.c
index 97dc8d1..e4d1ec8 100644
--- a/drivers/media/rc/ir-raw.c
+++ b/drivers/media/rc/ir-raw.c
@@ -18,6 +18,7 @@
#include <linux/kmod.h>
#include <linux/sched.h>
#include <linux/freezer.h>
+#include <linux/log2.h>
#include "rc-core-priv.h"
/* Define the max number of pulse/space transitions to buffer */
@@ -252,6 +253,8 @@ int ir_raw_event_register(struct rc_dev *dev)
{
int rc;
struct ir_raw_handler *handler;
+ int kfifo_size_order = order_base_2(sizeof(struct ir_raw_event) *
+ MAX_IR_EVENT_SIZE);
if (!dev)
return -EINVAL;
@@ -262,9 +265,7 @@ int ir_raw_event_register(struct rc_dev *dev)
dev->raw->dev = dev;
dev->raw->enabled_protocols = ~0;
- rc = kfifo_alloc(&dev->raw->kfifo,
- sizeof(struct ir_raw_event) * MAX_IR_EVENT_SIZE,
- GFP_KERNEL);
+ rc = kfifo_alloc(&dev->raw->kfifo, kfifo_size_order, GFP_KERNEL);
if (rc < 0)
goto out;
diff --git a/drivers/memstick/host/r592.h b/drivers/memstick/host/r592.h
index c5726c1..6fc19f4 100644
--- a/drivers/memstick/host/r592.h
+++ b/drivers/memstick/host/r592.h
@@ -143,7 +143,7 @@ struct r592_device {
struct task_struct *io_thread;
bool parallel_mode;
- DECLARE_KFIFO(pio_fifo, u8, sizeof(u32));
+ DECLARE_KFIFO(pio_fifo, u8, 2);
/* DMA area */
int dma_capable;
diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
index bd57a11..c54a7c5 100644
--- a/drivers/mmc/card/sdio_uart.c
+++ b/drivers/mmc/card/sdio_uart.c
@@ -43,12 +43,14 @@
#include <linux/mmc/card.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/sdio_ids.h>
+#include <linux/log2.h>
#define UART_NR 8 /* Number of UARTs this driver can handle */
#define FIFO_SIZE PAGE_SIZE
+#define FIFO_SIZE_ORDER PAGE_SHIFT
#define WAKEUP_CHARS 256
struct uart_icount {
@@ -93,7 +95,7 @@ static int sdio_uart_add_port(struct sdio_uart_port *port)
mutex_init(&port->func_lock);
spin_lock_init(&port->write_lock);
- if (kfifo_alloc(&port->xmit_fifo, FIFO_SIZE, GFP_KERNEL))
+ if (kfifo_alloc(&port->xmit_fifo, FIFO_SIZE_ORDER, GFP_KERNEL))
return -ENOMEM;
spin_lock(&sdio_uart_table_lock);
diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c
index 8dd6ba5..672ef47 100644
--- a/drivers/mtd/sm_ftl.c
+++ b/drivers/mtd/sm_ftl.c
@@ -17,6 +17,7 @@
#include <linux/bitops.h>
#include <linux/slab.h>
#include <linux/mtd/nand_ecc.h>
+#include <linux/log2.h>
#include "nand/sm_common.h"
#include "sm_ftl.h"
@@ -766,6 +767,7 @@ static int sm_init_zone(struct sm_ftl *ftl, int zone_num)
int lba;
int i = 0;
int len;
+ int kfifo_size_order;
dbg("initializing zone %d", zone_num);
@@ -778,7 +780,8 @@ static int sm_init_zone(struct sm_ftl *ftl, int zone_num)
/* Allocate memory for free sectors FIFO */
- if (kfifo_alloc(&zone->free_sectors, ftl->zone_size * 2, GFP_KERNEL)) {
+ kfifo_size_order = order_base_2(ftl->zone_size * 2);
+ if (kfifo_alloc(&zone->free_sectors, kfifo_size_order, GFP_KERNEL)) {
kfree(zone->lba_to_phys_table);
return -ENOMEM;
}
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
index 0c02f04..ea5ddf4 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/libertas/main.c
@@ -25,6 +25,8 @@
#include "cmd.h"
#include "mesh.h"
+#define KFIFO_SIZE_ORDER 6
+
#define DRIVER_RELEASE_VERSION "323.p0"
const char lbs_driver_version[] = "COMM-USB8388-" DRIVER_RELEASE_VERSION
#ifdef DEBUG
@@ -914,7 +916,7 @@ static int lbs_init_adapter(struct lbs_private *priv)
priv->resp_len[0] = priv->resp_len[1] = 0;
/* Create the event FIFO */
- ret = kfifo_alloc(&priv->event_fifo, sizeof(u32) * 16, GFP_KERNEL);
+ ret = kfifo_alloc(&priv->event_fifo, KFIFO_SIZE_ORDER, GFP_KERNEL);
if (ret) {
pr_err("Out of memory allocating event FIFO buffer\n");
goto out;
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index 44f8b3f..c8f68485 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -979,12 +979,11 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev)
* tx_queues * entry_num and round up to the nearest
* power of 2.
*/
- int kfifo_size =
- roundup_pow_of_two(rt2x00dev->ops->tx_queues *
+ int kfifo_size_order = order_base_2(rt2x00dev->ops->tx_queues *
rt2x00dev->ops->tx->entry_num *
sizeof(u32));
- status = kfifo_alloc(&rt2x00dev->txstatus_fifo, kfifo_size,
+ status = kfifo_alloc(&rt2x00dev->txstatus_fifo, kfifo_size_order,
GFP_KERNEL);
if (status)
return status;
diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c
index 421bbc5..ec9284a 100644
--- a/drivers/pci/pcie/aer/aerdrv_core.c
+++ b/drivers/pci/pcie/aer/aerdrv_core.c
@@ -574,7 +574,6 @@ static void handle_error_source(struct pcie_device *aerdev,
static void aer_recover_work_func(struct work_struct *work);
#define AER_RECOVER_RING_ORDER 4
-#define AER_RECOVER_RING_SIZE (1 << AER_RECOVER_RING_ORDER)
struct aer_recover_entry
{
@@ -585,7 +584,7 @@ struct aer_recover_entry
};
static DEFINE_KFIFO(aer_recover_ring, struct aer_recover_entry,
- AER_RECOVER_RING_SIZE);
+ AER_RECOVER_RING_ORDER);
/*
* Mutual exclusion for writers of aer_recover_ring, reader side don't
* need lock, because there is only one reader and lock is not needed
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c
index c4c1a54..185bd55 100644
--- a/drivers/platform/x86/fujitsu-laptop.c
+++ b/drivers/platform/x86/fujitsu-laptop.c
@@ -66,6 +66,7 @@
#include <linux/backlight.h>
#include <linux/input.h>
#include <linux/kfifo.h>
+#include <linux/log2.h>
#include <linux/video_output.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -116,6 +117,7 @@
#define MAX_HOTKEY_RINGBUFFER_SIZE 100
#define RINGBUFFERSIZE 40
+#define KFIFO_SIZE_ORDER (order_base_2(RINGBUFFERSIZE * sizeof(int)))
/* Debugging */
#define FUJLAPTOP_LOG ACPI_FUJITSU_HID ": "
@@ -825,8 +827,7 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
/* kfifo */
spin_lock_init(&fujitsu_hotkey->fifo_lock);
- error = kfifo_alloc(&fujitsu_hotkey->fifo, RINGBUFFERSIZE * sizeof(int),
- GFP_KERNEL);
+ error = kfifo_alloc(&fujitsu_hotkey->fifo, KFIFO_SIZE_ORDER, GFP_KERNEL);
if (error) {
pr_err("kfifo_alloc failed\n");
goto err_stop;
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
index daaddec..ee57eac 100644
--- a/drivers/platform/x86/sony-laptop.c
+++ b/drivers/platform/x86/sony-laptop.c
@@ -183,7 +183,7 @@ static void sony_nc_rfkill_update(void);
/*********** Input Devices ***********/
-#define SONY_LAPTOP_BUF_SIZE 128
+#define SONY_LAPTOP_KFIFO_SIZE_ORDER 7
struct sony_laptop_input_s {
atomic_t users;
struct input_dev *jog_dev;
@@ -447,7 +447,7 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device)
/* kfifo */
spin_lock_init(&sony_laptop_input.fifo_lock);
error = kfifo_alloc(&sony_laptop_input.fifo,
- SONY_LAPTOP_BUF_SIZE, GFP_KERNEL);
+ SONY_LAPTOP_KFIFO_SIZE_ORDER, GFP_KERNEL);
if (error) {
pr_err("kfifo_alloc failed\n");
goto err_dec_users;
@@ -3752,7 +3752,7 @@ static int sonypi_compat_init(void)
spin_lock_init(&sonypi_compat.fifo_lock);
error =
- kfifo_alloc(&sonypi_compat.fifo, SONY_LAPTOP_BUF_SIZE, GFP_KERNEL);
+ kfifo_alloc(&sonypi_compat.fifo, SONY_LAPTOP_KFIFO_SIZE_ORDER, GFP_KERNEL);
if (error) {
pr_err("kfifo_alloc failed\n");
return error;
diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c
index 6faba40..a731e87 100644
--- a/drivers/rapidio/devices/tsi721.c
+++ b/drivers/rapidio/devices/tsi721.c
@@ -32,6 +32,7 @@
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/kfifo.h>
+#include <linux/log2.h>
#include <linux/delay.h>
#include "tsi721.h"
@@ -970,11 +971,11 @@ static void tsi721_init_sr2pc_mapping(struct tsi721_device *priv)
*/
static int tsi721_port_write_init(struct tsi721_device *priv)
{
+ int kfifo_size_order = order_base_2(TSI721_RIO_PW_MSG_SIZE * 32);
priv->pw_discard_count = 0;
INIT_WORK(&priv->pw_work, tsi721_pw_dpc);
spin_lock_init(&priv->pw_fifo_lock);
- if (kfifo_alloc(&priv->pw_fifo,
- TSI721_RIO_PW_MSG_SIZE * 32, GFP_KERNEL)) {
+ if (kfifo_alloc(&priv->pw_fifo, kfifo_size_order, GFP_KERNEL)) {
dev_err(&priv->pdev->dev, "PW FIFO allocation failed\n");
return -ENOMEM;
}
diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c
index 552e8a2..bdb09bf 100644
--- a/drivers/scsi/libiscsi_tcp.c
+++ b/drivers/scsi/libiscsi_tcp.c
@@ -35,6 +35,7 @@
#include <linux/crypto.h>
#include <linux/delay.h>
#include <linux/kfifo.h>
+#include <linux/log2.h>
#include <linux/scatterlist.h>
#include <linux/module.h>
#include <net/tcp.h>
@@ -1113,6 +1114,7 @@ int iscsi_tcp_r2tpool_alloc(struct iscsi_session *session)
{
int i;
int cmd_i;
+ int kfifo_size_order;
/*
* initialize per-task: R2T pool and xmit queue
@@ -1135,8 +1137,8 @@ int iscsi_tcp_r2tpool_alloc(struct iscsi_session *session)
}
/* R2T xmit queue */
- if (kfifo_alloc(&tcp_task->r2tqueue,
- session->max_r2t * 4 * sizeof(void*), GFP_KERNEL)) {
+ kfifo_size_order = order_base_2(session->max_r2t * 4 * sizeof(void *));
+ if (kfifo_alloc(&tcp_task->r2tqueue, kfifo_size_order, GFP_KERNEL)) {
iscsi_pool_free(&tcp_task->r2tpool);
goto r2t_alloc_fail;
}
diff --git a/drivers/staging/omapdrm/omap_plane.c b/drivers/staging/omapdrm/omap_plane.c
index 2a8e5ba..40f057f 100644
--- a/drivers/staging/omapdrm/omap_plane.c
+++ b/drivers/staging/omapdrm/omap_plane.c
@@ -28,6 +28,8 @@
*/
#define omap_plane _omap_plane
+#define OMAP_KFIFO_SIZE_ORDER 4
+
/*
* plane funcs
*/
@@ -508,7 +510,8 @@ struct drm_plane *omap_plane_init(struct drm_device *dev,
mutex_init(&omap_plane->unpin_mutex);
- ret = kfifo_alloc(&omap_plane->unpin_fifo, 16, GFP_KERNEL);
+ ret = kfifo_alloc(&omap_plane->unpin_fifo, OMAP_KFIFO_SIZE_ORDER,
+ GFP_KERNEL);
if (ret) {
dev_err(dev->dev, "could not allocate unpin FIFO\n");
goto fail;
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index dcc0430..b3b1b1c 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -66,6 +66,8 @@
static int debug;
module_param(debug, int, 0600);
+#define KFIFO_SIZE_ORDER 12
+
/* Defaults: these are from the specification */
#define T1 10 /* 100mS */
@@ -1636,7 +1638,7 @@ static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr)
spin_lock_init(&dlci->lock);
mutex_init(&dlci->mutex);
dlci->fifo = &dlci->_fifo;
- if (kfifo_alloc(&dlci->_fifo, 4096, GFP_KERNEL) < 0) {
+ if (kfifo_alloc(&dlci->_fifo, KFIFO_SIZE_ORDER, GFP_KERNEL) < 0) {
kfree(dlci);
return NULL;
}
diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c
index a0c69ab..8b54da3 100644
--- a/drivers/tty/nozomi.c
+++ b/drivers/tty/nozomi.c
@@ -128,8 +128,7 @@ static int debug;
#define NTTY_TTY_MAXMINORS 256
#define NTTY_FIFO_BUFFER_SIZE 8192
-/* Must be power of 2 */
-#define FIFO_BUFFER_SIZE_UL 8192
+#define FIFO_BUFFER_SIZE_ORDER 13
/* Size of tmp send buffer to card */
#define SEND_BUF_MAX 1024
@@ -1428,7 +1427,7 @@ static int nozomi_card_init(struct pci_dev *pdev,
}
for (i = PORT_MDM; i < MAX_PORT; i++) {
- if (kfifo_alloc(&dc->port[i].fifo_ul, FIFO_BUFFER_SIZE_UL,
+ if (kfifo_alloc(&dc->port[i].fifo_ul, FIFO_BUFFER_SIZE_ORDER,
GFP_KERNEL)) {
dev_err(&pdev->dev,
"Could not allocate kfifo buffer\n");
diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c
index 675d94a..f80dc2c 100644
--- a/drivers/tty/serial/ifx6x60.c
+++ b/drivers/tty/serial/ifx6x60.c
@@ -880,7 +880,7 @@ static int ifx_spi_create_port(struct ifx_spi_device *ifx_dev)
lockdep_set_class_and_subclass(&ifx_dev->fifo_lock,
&ifx_spi_key, 0);
- if (kfifo_alloc(&ifx_dev->tx_fifo, IFX_SPI_FIFO_SIZE, GFP_KERNEL)) {
+ if (kfifo_alloc(&ifx_dev->tx_fifo, IFX_SPI_FIFO_SIZE_ORDER, GFP_KERNEL)) {
ret = -ENOMEM;
goto error_ret;
}
diff --git a/drivers/tty/serial/ifx6x60.h b/drivers/tty/serial/ifx6x60.h
index 4fbddc2..da4fd1c 100644
--- a/drivers/tty/serial/ifx6x60.h
+++ b/drivers/tty/serial/ifx6x60.h
@@ -31,7 +31,8 @@
#define IFX_SPI_MAX_MINORS 1
#define IFX_SPI_TRANSFER_SIZE 2048
-#define IFX_SPI_FIFO_SIZE 4096
+#define IFX_SPI_FIFO_SIZE_ORDER 12
+#define IFX_SPI_FIFO_SIZE (1 << IFX_SPI_FIFO_SIZE_ORDER)
#define IFX_SPI_HEADER_OVERHEAD 4
#define IFX_RESET_TIMEOUT msecs_to_jiffies(50)
diff --git a/drivers/tty/serial/kgdb_nmi.c b/drivers/tty/serial/kgdb_nmi.c
index 6ac2b79..947dd72 100644
--- a/drivers/tty/serial/kgdb_nmi.c
+++ b/drivers/tty/serial/kgdb_nmi.c
@@ -26,6 +26,7 @@
#include <linux/interrupt.h>
#include <linux/hrtimer.h>
#include <linux/tick.h>
+#include <linux/log2.h>
#include <linux/kfifo.h>
#include <linux/kgdb.h>
#include <linux/kdb.h>
@@ -75,13 +76,13 @@ static struct console kgdb_nmi_console = {
* This is usually the maximum rate on debug ports. We make fifo large enough
* to make copy-pasting to the terminal usable.
*/
-#define KGDB_NMI_BAUD 115200
-#define KGDB_NMI_FIFO_SIZE roundup_pow_of_two(KGDB_NMI_BAUD / 8 / HZ)
+#define KGDB_NMI_BAUD 115200
+#define KGDB_NMI_FIFO_SIZE_ORDER order_base_2(KGDB_NMI_BAUD / 8 / HZ)
struct kgdb_nmi_tty_priv {
struct tty_port port;
struct tasklet_struct tlet;
- STRUCT_KFIFO(char, KGDB_NMI_FIFO_SIZE) fifo;
+ STRUCT_KFIFO(char, KGDB_NMI_FIFO_SIZE_ORDER) fifo;
};
static struct kgdb_nmi_tty_priv *kgdb_nmi_port_to_priv(struct tty_port *port)
diff --git a/drivers/usb/host/fhci.h b/drivers/usb/host/fhci.h
index 7cc1c32..e4a0ac6 100644
--- a/drivers/usb/host/fhci.h
+++ b/drivers/usb/host/fhci.h
@@ -24,6 +24,7 @@
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/kfifo.h>
+#include <linux/log2.h>
#include <linux/io.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
@@ -478,7 +479,8 @@ static inline struct usb_hcd *fhci_to_hcd(struct fhci_hcd *fhci)
/* fifo of pointers */
static inline int cq_new(struct kfifo *fifo, int size)
{
- return kfifo_alloc(fifo, size * sizeof(void *), GFP_KERNEL);
+ int kfifo_size_order = order_base_2(size * sizeof(void *));
+ return kfifo_alloc(fifo, kfifo_size_order, GFP_KERNEL);
}
static inline void cq_delete(struct kfifo *kfifo)
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
index fd8c35f..a4d7cd1 100644
--- a/drivers/usb/serial/cypress_m8.c
+++ b/drivers/usb/serial/cypress_m8.c
@@ -54,7 +54,7 @@ static bool unstable_bauds;
#define DRIVER_DESC "Cypress USB to Serial Driver"
/* write buffer size defines */
-#define CYPRESS_BUF_SIZE 1024
+#define CYPRESS_KFIFO_SIZE_ORDER 10
static const struct usb_device_id id_table_earthmate[] = {
{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB) },
@@ -445,7 +445,7 @@ static int cypress_generic_port_probe(struct usb_serial_port *port)
priv->comm_is_ok = !0;
spin_lock_init(&priv->lock);
- if (kfifo_alloc(&priv->write_fifo, CYPRESS_BUF_SIZE, GFP_KERNEL)) {
+ if (kfifo_alloc(&priv->write_fifo, CYPRESS_KFIFO_SIZE_ORDER, GFP_KERNEL)) {
kfree(priv);
return -ENOMEM;
}
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index 58184f3..a19018b 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -64,7 +64,7 @@
#define EDGE_CLOSING_WAIT 4000 /* in .01 sec */
-#define EDGE_OUT_BUF_SIZE 1024
+#define EDGE_KFIFO_SIZE_ORDER 10
/* Product information read from the Edgeport */
@@ -2567,7 +2567,7 @@ static int edge_port_probe(struct usb_serial_port *port)
if (!edge_port)
return -ENOMEM;
- ret = kfifo_alloc(&edge_port->write_fifo, EDGE_OUT_BUF_SIZE,
+ ret = kfifo_alloc(&edge_port->write_fifo, EDGE_KFIFO_SIZE_ORDER,
GFP_KERNEL);
if (ret) {
kfree(edge_port);
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index f2530d2..777e90a 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -45,7 +45,8 @@
#define TI_FIRMWARE_BUF_SIZE 16284
-#define TI_WRITE_BUF_SIZE 1024
+#define TI_KFIFO_SIZE_ORDER 10
+#define TI_KFIFO_SIZE (1 << TI_KFIFO_SIZE_ORDER)
#define TI_TRANSFER_TIMEOUT 2
@@ -434,7 +435,7 @@ static int ti_port_probe(struct usb_serial_port *port)
tport->tp_closing_wait = closing_wait;
init_waitqueue_head(&tport->tp_msr_wait);
init_waitqueue_head(&tport->tp_write_wait);
- if (kfifo_alloc(&tport->write_fifo, TI_WRITE_BUF_SIZE, GFP_KERNEL)) {
+ if (kfifo_alloc(&tport->write_fifo, TI_KFIFO_SIZE_ORDER, GFP_KERNEL)) {
kfree(tport);
return -ENOMEM;
}
@@ -1355,7 +1356,7 @@ static int ti_get_serial_info(struct ti_port *tport,
ret_serial.line = port->serial->minor;
ret_serial.port = port->number - port->serial->minor;
ret_serial.flags = tport->tp_flags;
- ret_serial.xmit_fifo_size = TI_WRITE_BUF_SIZE;
+ ret_serial.xmit_fifo_size = TI_KFIFO_SIZE;
ret_serial.baud_base = tport->tp_tdev->td_is_3410 ? 921600 : 460800;
ret_serial.closing_wait = tport->tp_closing_wait;
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 64bda13..11ca271 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -934,7 +934,7 @@ static int usb_serial_probe(struct usb_interface *interface,
for (i = 0; i < num_bulk_out; ++i) {
endpoint = bulk_out_endpoint[i];
port = serial->port[i];
- if (kfifo_alloc(&port->write_fifo, PAGE_SIZE, GFP_KERNEL))
+ if (kfifo_alloc(&port->write_fifo, PAGE_SHIFT, GFP_KERNEL))
goto probe_error;
buffer_size = serial->type->bulk_out_size;
if (!buffer_size)
diff --git a/include/linux/kfifo.h b/include/linux/kfifo.h
index 4bf984e..28dfe98 100644
--- a/include/linux/kfifo.h
+++ b/include/linux/kfifo.h
@@ -76,8 +76,8 @@ struct __kfifo {
type buf[((size < 2) || (size & (size - 1))) ? -1 : size]; \
}
-#define STRUCT_KFIFO(type, size) \
- struct __STRUCT_KFIFO(type, size, 0)
+#define STRUCT_KFIFO(type, size_order) \
+ struct __STRUCT_KFIFO(type, (1<<(size_order)), 0)
#define __STRUCT_KFIFO_PTR(type, recsize) \
{ \
@@ -93,11 +93,11 @@ struct __kfifo {
*/
struct kfifo __STRUCT_KFIFO_PTR(unsigned char, 0);
-#define STRUCT_KFIFO_REC_1(size) \
- struct __STRUCT_KFIFO(unsigned char, size, 1)
+#define STRUCT_KFIFO_REC_1(size_order) \
+ struct __STRUCT_KFIFO(unsigned char, (1<<(size_order)), 1)
-#define STRUCT_KFIFO_REC_2(size) \
- struct __STRUCT_KFIFO(unsigned char, size, 2)
+#define STRUCT_KFIFO_REC_2(size_order) \
+ struct __STRUCT_KFIFO(unsigned char, (1<<(size_order)), 2)
/*
* define kfifo_rec types
@@ -123,9 +123,9 @@ struct kfifo_rec_ptr_2 __STRUCT_KFIFO_PTR(unsigned char, 2);
* DECLARE_KFIFO - macro to declare a fifo object
* @fifo: name of the declared fifo
* @type: type of the fifo elements
- * @size: the number of elements in the fifo, this must be a power of 2
+ * @size_order: request 2^size_order fifo elements
*/
-#define DECLARE_KFIFO(fifo, type, size) STRUCT_KFIFO(type, size) fifo
+#define DECLARE_KFIFO(fifo, type, size_order) STRUCT_KFIFO(type, size_order) fifo
/**
* INIT_KFIFO - Initialize a fifo declared by DECLARE_KFIFO
@@ -146,12 +146,12 @@ struct kfifo_rec_ptr_2 __STRUCT_KFIFO_PTR(unsigned char, 2);
* DEFINE_KFIFO - macro to define and initialize a fifo
* @fifo: name of the declared fifo datatype
* @type: type of the fifo elements
- * @size: the number of elements in the fifo, this must be a power of 2
+ * @size_order: request 2^size_order fifo elements
*
* Note: the macro can be used for global and local fifo data type variables.
*/
-#define DEFINE_KFIFO(fifo, type, size) \
- DECLARE_KFIFO(fifo, type, size) = \
+#define DEFINE_KFIFO(fifo, type, size_order) \
+ DECLARE_KFIFO(fifo, type, size_order) = \
(typeof(fifo)) { \
{ \
{ \
@@ -317,22 +317,21 @@ __kfifo_uint_must_check_helper( \
/**
* kfifo_alloc - dynamically allocates a new fifo buffer
* @fifo: pointer to the fifo
- * @size: the number of elements in the fifo, this must be a power of 2
+ * @size_order: request 2^size_order fifo elements
* @gfp_mask: get_free_pages mask, passed to kmalloc()
*
* This macro dynamically allocates a new fifo buffer.
*
- * The numer of elements will be rounded-up to a power of 2.
* The fifo will be release with kfifo_free().
* Return 0 if no error, otherwise an error code.
*/
-#define kfifo_alloc(fifo, size, gfp_mask) \
+#define kfifo_alloc(fifo, size_order, gfp_mask) \
__kfifo_int_must_check_helper( \
({ \
typeof((fifo) + 1) __tmp = (fifo); \
struct __kfifo *__kfifo = &__tmp->kfifo; \
__is_kfifo_ptr(__tmp) ? \
- __kfifo_alloc(__kfifo, size, sizeof(*__tmp->type), gfp_mask) : \
+ __kfifo_alloc(__kfifo, size_order, sizeof(*__tmp->type), gfp_mask) : \
-EINVAL; \
}) \
)
@@ -745,7 +744,7 @@ __kfifo_uint_must_check_helper( \
}) \
)
-extern int __kfifo_alloc(struct __kfifo *fifo, unsigned int size,
+extern int __kfifo_alloc(struct __kfifo *fifo, int size_order,
size_t esize, gfp_t gfp_mask);
extern void __kfifo_free(struct __kfifo *fifo);
diff --git a/include/linux/rio.h b/include/linux/rio.h
index a3e7842..05ff6bb 100644
--- a/include/linux/rio.h
+++ b/include/linux/rio.h
@@ -70,6 +70,7 @@
#define RIO_OUTB_MBOX_RESOURCE 2
#define RIO_PW_MSG_SIZE 64
+#define RIO_KFIFO_SIZE_ORDER 11 /* 64 * 32 */
/*
* A component tag value (stored in the component tag CSR) is used as device's
diff --git a/include/media/lirc_dev.h b/include/media/lirc_dev.h
index 168dd0b..7816d39 100644
--- a/include/media/lirc_dev.h
+++ b/include/media/lirc_dev.h
@@ -19,6 +19,7 @@
#include <linux/ioctl.h>
#include <linux/poll.h>
#include <linux/kfifo.h>
+#include <linux/log2.h>
#include <media/lirc.h>
struct lirc_buffer {
@@ -50,12 +51,13 @@ static inline int lirc_buffer_init(struct lirc_buffer *buf,
unsigned int size)
{
int ret;
+ int kfifo_size_order = order_base_2(size * chunk_size);
init_waitqueue_head(&buf->wait_poll);
spin_lock_init(&buf->fifo_lock);
buf->chunk_size = chunk_size;
buf->size = size;
- ret = kfifo_alloc(&buf->fifo, size * chunk_size, GFP_KERNEL);
+ ret = kfifo_alloc(&buf->fifo, kfifo_size_order, GFP_KERNEL);
if (ret == 0)
buf->fifo_initialized = 1;
diff --git a/kernel/kfifo.c b/kernel/kfifo.c
index d07f480..be1c2a0 100644
--- a/kernel/kfifo.c
+++ b/kernel/kfifo.c
@@ -35,15 +35,10 @@ static inline unsigned int kfifo_unused(struct __kfifo *fifo)
return (fifo->mask + 1) - (fifo->in - fifo->out);
}
-int __kfifo_alloc(struct __kfifo *fifo, unsigned int size,
+int __kfifo_alloc(struct __kfifo *fifo, int size_order,
size_t esize, gfp_t gfp_mask)
{
- /*
- * round down to the next power of 2, since our 'let the indices
- * wrap' technique works only in this case.
- */
- if (!is_power_of_2(size))
- size = rounddown_pow_of_two(size);
+ unsigned int size = 1 << size_order;
fifo->in = 0;
fifo->out = 0;
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index c6e4dd3..827bbf3 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1189,7 +1189,6 @@ out:
EXPORT_SYMBOL_GPL(memory_failure);
#define MEMORY_FAILURE_FIFO_ORDER 4
-#define MEMORY_FAILURE_FIFO_SIZE (1 << MEMORY_FAILURE_FIFO_ORDER)
struct memory_failure_entry {
unsigned long pfn;
@@ -1199,7 +1198,7 @@ struct memory_failure_entry {
struct memory_failure_cpu {
DECLARE_KFIFO(fifo, struct memory_failure_entry,
- MEMORY_FAILURE_FIFO_SIZE);
+ MEMORY_FAILURE_FIFO_ORDER);
spinlock_t lock;
struct work_struct work;
};
diff --git a/net/dccp/probe.c b/net/dccp/probe.c
index 0a8d6eb..0a12fd5 100644
--- a/net/dccp/probe.c
+++ b/net/dccp/probe.c
@@ -31,6 +31,7 @@
#include <linux/kfifo.h>
#include <linux/vmalloc.h>
#include <linux/gfp.h>
+#include <linux/log2.h>
#include <net/net_namespace.h>
#include "dccp.h"
@@ -166,10 +167,11 @@ static __init int setup_jprobe(void)
static __init int dccpprobe_init(void)
{
int ret = -ENOMEM;
+ int kfifo_size_order = order_base_2(bufsize);
init_waitqueue_head(&dccpw.wait);
spin_lock_init(&dccpw.lock);
- if (kfifo_alloc(&dccpw.fifo, bufsize, GFP_KERNEL))
+ if (kfifo_alloc(&dccpw.fifo, kfifo_size_order, GFP_KERNEL))
return ret;
if (!proc_net_fops_create(&init_net, procname, S_IRUSR, &dccpprobe_fops))
goto err0;
@@ -200,7 +202,7 @@ module_exit(dccpprobe_exit);
MODULE_PARM_DESC(port, "Port to match (0=all)");
module_param(port, int, 0);
-MODULE_PARM_DESC(bufsize, "Log buffer size (default 64k)");
+MODULE_PARM_DESC(bufsize, "Log buffer size (default 64k , should be power of 2. If not, will roundup to power of 2)");
module_param(bufsize, int, 0);
MODULE_AUTHOR("Ian McDonald <ian.mcdonald-WpNOhQBWTy6lP80pJB477g@public.gmane.org>");
diff --git a/net/sctp/probe.c b/net/sctp/probe.c
index 5f7518d..1736ef4 100644
--- a/net/sctp/probe.c
+++ b/net/sctp/probe.c
@@ -33,6 +33,7 @@
#include <linux/module.h>
#include <linux/kfifo.h>
#include <linux/time.h>
+#include <linux/log2.h>
#include <net/net_namespace.h>
#include <net/sctp/sctp.h>
@@ -47,7 +48,7 @@ MODULE_PARM_DESC(port, "Port to match (0=all)");
module_param(port, int, 0);
static int bufsize __read_mostly = 64 * 1024;
-MODULE_PARM_DESC(bufsize, "Log buffer size (default 64k)");
+MODULE_PARM_DESC(bufsize, "Log buffer size (default 64k, should be power of 2. If not, will roundup to power of 2)");
module_param(bufsize, int, 0);
static int full __read_mostly = 1;
@@ -182,10 +183,11 @@ static struct jprobe sctp_recv_probe = {
static __init int sctpprobe_init(void)
{
int ret = -ENOMEM;
+ int kfifo_size_order = order_base_2(bufsize);
init_waitqueue_head(&sctpw.wait);
spin_lock_init(&sctpw.lock);
- if (kfifo_alloc(&sctpw.fifo, bufsize, GFP_KERNEL))
+ if (kfifo_alloc(&sctpw.fifo, kfifo_size_order, GFP_KERNEL))
return ret;
if (!proc_net_fops_create(&init_net, procname, S_IRUSR,
diff --git a/samples/kfifo/bytestream-example.c b/samples/kfifo/bytestream-example.c
index cfe40ad..eb3a46e 100644
--- a/samples/kfifo/bytestream-example.c
+++ b/samples/kfifo/bytestream-example.c
@@ -18,7 +18,7 @@
*/
/* fifo size in elements (bytes) */
-#define FIFO_SIZE 32
+#define FIFO_SIZE_ORDER 5
/* name of the proc entry */
#define PROC_FIFO "bytestream-fifo"
@@ -41,10 +41,10 @@ static DEFINE_MUTEX(write_lock);
#ifdef DYNAMIC
static struct kfifo test;
#else
-static DECLARE_KFIFO(test, unsigned char, FIFO_SIZE);
+static DECLARE_KFIFO(test, unsigned char, FIFO_SIZE_ORDER);
#endif
-static const unsigned char expected_result[FIFO_SIZE] = {
+static const unsigned char expected_result[1<<FIFO_SIZE_ORDER] = {
3, 4, 5, 6, 7, 8, 9, 0,
1, 20, 21, 22, 23, 24, 25, 26,
27, 28, 29, 30, 31, 32, 33, 34,
@@ -156,7 +156,7 @@ static int __init example_init(void)
#ifdef DYNAMIC
int ret;
- ret = kfifo_alloc(&test, FIFO_SIZE, GFP_KERNEL);
+ ret = kfifo_alloc(&test, FIFO_SIZE_ORDER, GFP_KERNEL);
if (ret) {
printk(KERN_ERR "error kfifo_alloc\n");
return ret;
diff --git a/samples/kfifo/dma-example.c b/samples/kfifo/dma-example.c
index 0647379..bbc0787 100644
--- a/samples/kfifo/dma-example.c
+++ b/samples/kfifo/dma-example.c
@@ -16,7 +16,8 @@
*/
/* fifo size in elements (bytes) */
-#define FIFO_SIZE 32
+#define FIFO_SIZE_ORDER 5
+#define FIFO_SIZE (1<< FIFO_SIZE_ORDER)
static struct kfifo fifo;
@@ -29,7 +30,7 @@ static int __init example_init(void)
printk(KERN_INFO "DMA fifo test start\n");
- if (kfifo_alloc(&fifo, FIFO_SIZE, GFP_KERNEL)) {
+ if (kfifo_alloc(&fifo, FIFO_SIZE_ORDER, GFP_KERNEL)) {
printk(KERN_WARNING "error kfifo_alloc\n");
return -ENOMEM;
}
diff --git a/samples/kfifo/inttype-example.c b/samples/kfifo/inttype-example.c
index 6f8e79e..bed3229 100644
--- a/samples/kfifo/inttype-example.c
+++ b/samples/kfifo/inttype-example.c
@@ -18,7 +18,8 @@
*/
/* fifo size in elements (ints) */
-#define FIFO_SIZE 32
+#define FIFO_SIZE_ORDER 5
+#define FIFO_SIZE (1<< FIFO_SIZE_ORDER)
/* name of the proc entry */
#define PROC_FIFO "int-fifo"
@@ -41,7 +42,7 @@ static DEFINE_MUTEX(write_lock);
#ifdef DYNAMIC
static DECLARE_KFIFO_PTR(test, int);
#else
-static DEFINE_KFIFO(test, int, FIFO_SIZE);
+static DEFINE_KFIFO(test, int, FIFO_SIZE_ORDER);
#endif
static const int expected_result[FIFO_SIZE] = {
@@ -149,7 +150,7 @@ static int __init example_init(void)
#ifdef DYNAMIC
int ret;
- ret = kfifo_alloc(&test, FIFO_SIZE, GFP_KERNEL);
+ ret = kfifo_alloc(&test, FIFO_SIZE_ORDER, GFP_KERNEL);
if (ret) {
printk(KERN_ERR "error kfifo_alloc\n");
return ret;
diff --git a/samples/kfifo/record-example.c b/samples/kfifo/record-example.c
index 2d7529e..2902eae 100644
--- a/samples/kfifo/record-example.c
+++ b/samples/kfifo/record-example.c
@@ -18,7 +18,7 @@
*/
/* fifo size in elements (bytes) */
-#define FIFO_SIZE 128
+#define FIFO_SIZE_ORDER 7
/* name of the proc entry */
#define PROC_FIFO "record-fifo"
@@ -50,7 +50,7 @@ static DEFINE_MUTEX(write_lock);
struct kfifo_rec_ptr_1 test;
#else
-typedef STRUCT_KFIFO_REC_1(FIFO_SIZE) mytest;
+typedef STRUCT_KFIFO_REC_1(FIFO_SIZE_ORDER) mytest;
static mytest test;
#endif
@@ -163,7 +163,7 @@ static int __init example_init(void)
#ifdef DYNAMIC
int ret;
- ret = kfifo_alloc(&test, FIFO_SIZE, GFP_KERNEL);
+ ret = kfifo_alloc(&test, FIFO_SIZE_ORDER, GFP_KERNEL);
if (ret) {
printk(KERN_ERR "error kfifo_alloc\n");
return ret;
--
1.7.7.6
--
You received this message because you are subscribed to the Google Groups "open-iscsi" group.
To post to this group, send email to open-iscsi-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
To unsubscribe from this group, send email to open-iscsi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit this group at http://groups.google.com/group/open-iscsi?hl=en.
^ permalink raw reply related
* [PATCH V4 RESEND] serial: tegra: add serial driver
From: Laxman Dewangan @ 2013-01-08 10:57 UTC (permalink / raw)
To: alan-VuQAYsv1563Yd54FQh9/CA,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r, jslaby-AlSwsSmVLrQ
Cc: linux-doc-u79uwXL29TY76Z2rM5mHXA,
devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
rob.herring-bsGFqQB8/DxBDgjK7y7TUQ,
ldewangan-DDmLM1+adcrQT0dZR+AlfA,
linux-serial-u79uwXL29TY76Z2rM5mHXA,
linux-tegra-u79uwXL29TY76Z2rM5mHXA
NVIDIA's Tegra has multiple UART controller which supports:
- APB DMA based controller fifo read/write.
- End Of Data interrupt in incoming data to know whether end
of frame achieve or not.
- HW controlled RTS and CTS flow control to reduce SW overhead.
Add serial driver to use all above feature.
Signed-off-by: Laxman Dewangan <ldewangan-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
Acked-by: Alan Cox <alan-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
---
Changes from V1:
- Remove port-number parameter and use the of_alias_get().
- put the ref count for the tty.
- rename the binding document file to serial-tegra.txt to match with
driver name.
- Remove falsy introduced line from Kconfig.
- Move platform data file to linux/platfor_data. Not removing the
platform datacompletely now. if it is requie to remove the will
be remove later along with other tegra driver also.
- Simplify tegra_uart_set_mctrl
- Clear flag for CMSPAR as driver dose not support this.
- Modify uart_get_baud_rate() to use actual baudrate.
- reorder compatibles in documentation file.
- used of_property_read_bool for modem interrupt.
- remove check if (pdev->dev.of_node) as it si always true.
- Drop devinit and devexit compiler option.
- nit cleanups for moving struture to the usage area.
Changes from V2:
- Rename the binding document to nvidia,tegra20-hsuart.txt
- Some nit cleanups and use Caps for aliases like APB, DMA, UART.
- Adding Alan's ack.
Changes from V3:
- Dropped support for platform data and hence remove header file.
- In clk_get, pass NULL for connection name.
Resending patch as V4 version was sent before long holiday (19 dec) and
in the case of if it is missed. Just change the copyright year for this
resend to have 2013.
.../bindings/serial/nvidia,tegra20-hsuart.txt | 24 +
drivers/tty/serial/Kconfig | 11 +
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/serial-tegra.c | 1399 ++++++++++++++++++++
4 files changed, 1435 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt
create mode 100644 drivers/tty/serial/serial-tegra.c
diff --git a/Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt b/Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt
new file mode 100644
index 0000000..392a449
--- /dev/null
+++ b/Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt
@@ -0,0 +1,24 @@
+NVIDIA Tegra20/Tegra30 high speed (DMA based) UART controller driver.
+
+Required properties:
+- compatible : should be "nvidia,tegra30-hsuart", "nvidia,tegra20-hsuart".
+- reg: Should contain UART controller registers location and length.
+- interrupts: Should contain UART controller interrupts.
+- nvidia,dma-request-selector : The Tegra DMA controller's phandle and
+ request selector for this UART controller.
+
+Optional properties:
+- nvidia,enable-modem-interrupt: Enable modem interrupts. Should be enable
+ only if all 8 lines of UART controller are pinmuxed.
+
+Example:
+
+serial@70006000 {
+ compatible = "nvidia,tegra30-hsuart", "nvidia,tegra20-hsuart";
+ reg = <0x70006000 0x40>;
+ reg-shift = <2>;
+ interrupts = <0 36 0x04>;
+ nvidia,dma-request-selector = <&apbdma 8>;
+ nvidia,enable-modem-interrupt;
+ status = "disabled";
+};
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 59c23d0..aff3cd3 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -269,6 +269,17 @@ config SERIAL_SIRFSOC_CONSOLE
your boot loader about how to pass options to the kernel at
boot time.)
+config SERIAL_TEGRA
+ tristate "NVIDIA Tegra20/30 SoC serial controller"
+ depends on ARCH_TEGRA && TEGRA20_APB_DMA
+ select SERIAL_CORE
+ help
+ Support for the on-chip UARTs on the NVIDIA Tegra series SOCs
+ providing /dev/ttyHS0, 1, 2, 3 and 4 (note, some machines may not
+ provide all of these ports, depending on how the serial port
+ are enabled). This driver uses the APB DMA to achieve higher baudrate
+ and better performance.
+
config SERIAL_MAX3100
tristate "MAX3100 support"
depends on SPI
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index df1b998..82e4306 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -80,6 +80,7 @@ obj-$(CONFIG_SERIAL_MXS_AUART) += mxs-auart.o
obj-$(CONFIG_SERIAL_LANTIQ) += lantiq.o
obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o
obj-$(CONFIG_SERIAL_SIRFSOC) += sirfsoc_uart.o
+obj-$(CONFIG_SERIAL_TEGRA) += serial-tegra.o
obj-$(CONFIG_SERIAL_AR933X) += ar933x_uart.o
obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o
obj-$(CONFIG_SERIAL_ARC) += arc_uart.o
diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c
new file mode 100644
index 0000000..ad094d2
--- /dev/null
+++ b/drivers/tty/serial/serial-tegra.c
@@ -0,0 +1,1399 @@
+/*
+ * serial_tegra.c
+ *
+ * High-speed serial driver for NVIDIA Tegra SoCs
+ *
+ * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Author: Laxman Dewangan <ldewangan-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pagemap.h>
+#include <linux/platform_device.h>
+#include <linux/serial.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/termios.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#include <mach/clk.h>
+
+#define TEGRA_UART_TYPE "TEGRA_UART"
+#define TX_EMPTY_STATUS (UART_LSR_TEMT | UART_LSR_THRE)
+#define BYTES_TO_ALIGN(x) ((unsigned long)(x) & 0x3)
+
+#define TEGRA_UART_RX_DMA_BUFFER_SIZE 4096
+#define TEGRA_UART_LSR_TXFIFO_FULL 0x100
+#define TEGRA_UART_IER_EORD 0x20
+#define TEGRA_UART_MCR_RTS_EN 0x40
+#define TEGRA_UART_MCR_CTS_EN 0x20
+#define TEGRA_UART_LSR_ANY (UART_LSR_OE | UART_LSR_BI | \
+ UART_LSR_PE | UART_LSR_FE)
+#define TEGRA_UART_IRDA_CSR 0x08
+#define TEGRA_UART_SIR_ENABLED 0x80
+
+#define TEGRA_UART_TX_PIO 1
+#define TEGRA_UART_TX_DMA 2
+#define TEGRA_UART_MIN_DMA 16
+#define TEGRA_UART_FIFO_SIZE 32
+
+/*
+ * Tx fifo trigger level setting in tegra uart is in
+ * reverse way then conventional uart.
+ */
+#define TEGRA_UART_TX_TRIG_16B 0x00
+#define TEGRA_UART_TX_TRIG_8B 0x10
+#define TEGRA_UART_TX_TRIG_4B 0x20
+#define TEGRA_UART_TX_TRIG_1B 0x30
+
+#define TEGRA_UART_MAXIMUM 5
+
+/* Default UART setting when started: 115200 no parity, stop, 8 data bits */
+#define TEGRA_UART_DEFAULT_BAUD 115200
+#define TEGRA_UART_DEFAULT_LSR UART_LCR_WLEN8
+
+/* Tx transfer mode */
+#define TEGRA_TX_PIO 1
+#define TEGRA_TX_DMA 2
+
+/**
+ * tegra_uart_chip_data: SOC specific data.
+ *
+ * @tx_fifo_full_status: Status flag available for checking tx fifo full.
+ * @allow_txfifo_reset_fifo_mode: allow_tx fifo reset with fifo mode or not.
+ * Tegra30 does not allow this.
+ * @support_clk_src_div: Clock source support the clock divider.
+ */
+struct tegra_uart_chip_data {
+ bool tx_fifo_full_status;
+ bool allow_txfifo_reset_fifo_mode;
+ bool support_clk_src_div;
+};
+
+struct tegra_uart_port {
+ struct uart_port uport;
+ const struct tegra_uart_chip_data *cdata;
+
+ struct clk *uart_clk;
+ unsigned int current_baud;
+
+ /* Register shadow */
+ unsigned long fcr_shadow;
+ unsigned long mcr_shadow;
+ unsigned long lcr_shadow;
+ unsigned long ier_shadow;
+ bool rts_active;
+
+ int tx_in_progress;
+ unsigned int tx_bytes;
+
+ bool enable_modem_interrupt;
+
+ bool rx_timeout;
+ int rx_in_progress;
+ int symb_bit;
+ int dma_req_sel;
+
+ struct dma_chan *rx_dma_chan;
+ struct dma_chan *tx_dma_chan;
+ dma_addr_t rx_dma_buf_phys;
+ dma_addr_t tx_dma_buf_phys;
+ unsigned char *rx_dma_buf_virt;
+ unsigned char *tx_dma_buf_virt;
+ struct dma_async_tx_descriptor *tx_dma_desc;
+ struct dma_async_tx_descriptor *rx_dma_desc;
+ dma_cookie_t tx_cookie;
+ dma_cookie_t rx_cookie;
+ int tx_bytes_requested;
+ int rx_bytes_requested;
+};
+
+static void tegra_uart_start_next_tx(struct tegra_uart_port *tup);
+static int tegra_uart_start_rx_dma(struct tegra_uart_port *tup);
+
+static inline unsigned long tegra_uart_read(struct tegra_uart_port *tup,
+ unsigned long reg)
+{
+ return readl(tup->uport.membase + (reg << tup->uport.regshift));
+}
+
+static inline void tegra_uart_write(struct tegra_uart_port *tup, unsigned val,
+ unsigned long reg)
+{
+ writel(val, tup->uport.membase + (reg << tup->uport.regshift));
+}
+
+static inline struct tegra_uart_port *to_tegra_uport(struct uart_port *u)
+{
+ return container_of(u, struct tegra_uart_port, uport);
+}
+
+static unsigned int tegra_uart_get_mctrl(struct uart_port *u)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+
+ /*
+ * RI - Ring detector is active
+ * CD/DCD/CAR - Carrier detect is always active. For some reason
+ * linux has different names for carrier detect.
+ * DSR - Data Set ready is active as the hardware doesn't support it.
+ * Don't know if the linux support this yet?
+ * CTS - Clear to send. Always set to active, as the hardware handles
+ * CTS automatically.
+ */
+ if (tup->enable_modem_interrupt)
+ return TIOCM_RI | TIOCM_CD | TIOCM_DSR | TIOCM_CTS;
+ return TIOCM_CTS;
+}
+
+static void set_rts(struct tegra_uart_port *tup, bool active)
+{
+ unsigned long mcr;
+
+ mcr = tup->mcr_shadow;
+ if (active)
+ mcr |= TEGRA_UART_MCR_RTS_EN;
+ else
+ mcr &= ~TEGRA_UART_MCR_RTS_EN;
+ if (mcr != tup->mcr_shadow) {
+ tegra_uart_write(tup, mcr, UART_MCR);
+ tup->mcr_shadow = mcr;
+ }
+ return;
+}
+
+static void set_dtr(struct tegra_uart_port *tup, bool active)
+{
+ unsigned long mcr;
+
+ mcr = tup->mcr_shadow;
+ if (active)
+ mcr |= UART_MCR_DTR;
+ else
+ mcr &= ~UART_MCR_DTR;
+ if (mcr != tup->mcr_shadow) {
+ tegra_uart_write(tup, mcr, UART_MCR);
+ tup->mcr_shadow = mcr;
+ }
+ return;
+}
+
+static void tegra_uart_set_mctrl(struct uart_port *u, unsigned int mctrl)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+ unsigned long mcr;
+ int dtr_enable;
+
+ mcr = tup->mcr_shadow;
+ tup->rts_active = !!(mctrl & TIOCM_RTS);
+ set_rts(tup, tup->rts_active);
+
+ dtr_enable = !!(mctrl & TIOCM_DTR);
+ set_dtr(tup, dtr_enable);
+ return;
+}
+
+static void tegra_uart_break_ctl(struct uart_port *u, int break_ctl)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+ unsigned long lcr;
+
+ lcr = tup->lcr_shadow;
+ if (break_ctl)
+ lcr |= UART_LCR_SBC;
+ else
+ lcr &= ~UART_LCR_SBC;
+ tegra_uart_write(tup, lcr, UART_LCR);
+ tup->lcr_shadow = lcr;
+}
+
+/* Wait for a symbol-time. */
+static void tegra_uart_wait_sym_time(struct tegra_uart_port *tup,
+ unsigned int syms)
+{
+ if (tup->current_baud)
+ udelay(DIV_ROUND_UP(syms * tup->symb_bit * 1000000,
+ tup->current_baud));
+}
+
+static void tegra_uart_fifo_reset(struct tegra_uart_port *tup, u8 fcr_bits)
+{
+ unsigned long fcr = tup->fcr_shadow;
+
+ if (tup->cdata->allow_txfifo_reset_fifo_mode) {
+ fcr |= fcr_bits & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+ tegra_uart_write(tup, fcr, UART_FCR);
+ } else {
+ fcr &= ~UART_FCR_ENABLE_FIFO;
+ tegra_uart_write(tup, fcr, UART_FCR);
+ udelay(60);
+ fcr |= fcr_bits & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+ tegra_uart_write(tup, fcr, UART_FCR);
+ fcr |= UART_FCR_ENABLE_FIFO;
+ tegra_uart_write(tup, fcr, UART_FCR);
+ }
+
+ /* Dummy read to ensure the write is posted */
+ tegra_uart_read(tup, UART_SCR);
+
+ /* Wait for the flush to propagate. */
+ tegra_uart_wait_sym_time(tup, 1);
+}
+
+static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud)
+{
+ unsigned long rate;
+ unsigned int divisor;
+ unsigned long lcr;
+ int ret;
+
+ if (tup->current_baud == baud)
+ return 0;
+
+ if (tup->cdata->support_clk_src_div) {
+ rate = baud * 16;
+ ret = clk_set_rate(tup->uart_clk, rate);
+ if (ret < 0) {
+ dev_err(tup->uport.dev,
+ "clk_set_rate() failed for rate %lu\n", rate);
+ return ret;
+ }
+ divisor = 1;
+ } else {
+ rate = clk_get_rate(tup->uart_clk);
+ divisor = DIV_ROUND_CLOSEST(rate, baud * 16);
+ }
+
+ lcr = tup->lcr_shadow;
+ lcr |= UART_LCR_DLAB;
+ tegra_uart_write(tup, lcr, UART_LCR);
+
+ tegra_uart_write(tup, divisor & 0xFF, UART_TX);
+ tegra_uart_write(tup, ((divisor >> 8) & 0xFF), UART_IER);
+
+ lcr &= ~UART_LCR_DLAB;
+ tegra_uart_write(tup, lcr, UART_LCR);
+
+ /* Dummy read to ensure the write is posted */
+ tegra_uart_read(tup, UART_SCR);
+
+ tup->current_baud = baud;
+
+ /* wait two character intervals at new rate */
+ tegra_uart_wait_sym_time(tup, 2);
+ return 0;
+}
+
+static char tegra_uart_decode_rx_error(struct tegra_uart_port *tup,
+ unsigned long lsr)
+{
+ char flag = TTY_NORMAL;
+
+ if (unlikely(lsr & TEGRA_UART_LSR_ANY)) {
+ if (lsr & UART_LSR_OE) {
+ /* Overrrun error */
+ flag |= TTY_OVERRUN;
+ tup->uport.icount.overrun++;
+ dev_err(tup->uport.dev, "Got overrun errors\n");
+ } else if (lsr & UART_LSR_PE) {
+ /* Parity error */
+ flag |= TTY_PARITY;
+ tup->uport.icount.parity++;
+ dev_err(tup->uport.dev, "Got Parity errors\n");
+ } else if (lsr & UART_LSR_FE) {
+ flag |= TTY_FRAME;
+ tup->uport.icount.frame++;
+ dev_err(tup->uport.dev, "Got frame errors\n");
+ } else if (lsr & UART_LSR_BI) {
+ dev_err(tup->uport.dev, "Got Break\n");
+ tup->uport.icount.brk++;
+ /* If FIFO read error without any data, reset Rx FIFO */
+ if (!(lsr & UART_LSR_DR) && (lsr & UART_LSR_FIFOE))
+ tegra_uart_fifo_reset(tup, UART_FCR_CLEAR_RCVR);
+ }
+ }
+ return flag;
+}
+
+static int tegra_uart_request_port(struct uart_port *u)
+{
+ return 0;
+}
+
+static void tegra_uart_release_port(struct uart_port *u)
+{
+ /* Nothing to do here */
+}
+
+static void tegra_uart_fill_tx_fifo(struct tegra_uart_port *tup, int max_bytes)
+{
+ struct circ_buf *xmit = &tup->uport.state->xmit;
+ int i;
+
+ for (i = 0; i < max_bytes; i++) {
+ BUG_ON(uart_circ_empty(xmit));
+ if (tup->cdata->tx_fifo_full_status) {
+ unsigned long lsr = tegra_uart_read(tup, UART_LSR);
+ if ((lsr & TEGRA_UART_LSR_TXFIFO_FULL))
+ break;
+ }
+ tegra_uart_write(tup, xmit->buf[xmit->tail], UART_TX);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ tup->uport.icount.tx++;
+ }
+}
+
+static void tegra_uart_start_pio_tx(struct tegra_uart_port *tup,
+ unsigned int bytes)
+{
+ if (bytes > TEGRA_UART_MIN_DMA)
+ bytes = TEGRA_UART_MIN_DMA;
+
+ tup->tx_in_progress = TEGRA_UART_TX_PIO;
+ tup->tx_bytes = bytes;
+ tup->ier_shadow |= UART_IER_THRI;
+ tegra_uart_write(tup, tup->ier_shadow, UART_IER);
+}
+
+static void tegra_uart_tx_dma_complete(void *args)
+{
+ struct tegra_uart_port *tup = args;
+ struct circ_buf *xmit = &tup->uport.state->xmit;
+ struct dma_tx_state state;
+ unsigned long flags;
+ int count;
+
+ dmaengine_tx_status(tup->tx_dma_chan, tup->rx_cookie, &state);
+ count = tup->tx_bytes_requested - state.residue;
+ async_tx_ack(tup->tx_dma_desc);
+ spin_lock_irqsave(&tup->uport.lock, flags);
+ xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
+ tup->tx_in_progress = 0;
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&tup->uport);
+ tegra_uart_start_next_tx(tup);
+ spin_unlock_irqrestore(&tup->uport.lock, flags);
+}
+
+static int tegra_uart_start_tx_dma(struct tegra_uart_port *tup,
+ unsigned long count)
+{
+ struct circ_buf *xmit = &tup->uport.state->xmit;
+ dma_addr_t tx_phys_addr;
+
+ dma_sync_single_for_device(tup->uport.dev, tup->tx_dma_buf_phys,
+ UART_XMIT_SIZE, DMA_TO_DEVICE);
+
+ tup->tx_bytes = count & ~(0xF);
+ tx_phys_addr = tup->tx_dma_buf_phys + xmit->tail;
+ tup->tx_dma_desc = dmaengine_prep_slave_single(tup->tx_dma_chan,
+ tx_phys_addr, tup->tx_bytes, DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT);
+ if (!tup->tx_dma_desc) {
+ dev_err(tup->uport.dev, "Not able to get desc for Tx\n");
+ return -EIO;
+ }
+
+ tup->tx_dma_desc->callback = tegra_uart_tx_dma_complete;
+ tup->tx_dma_desc->callback_param = tup;
+ tup->tx_in_progress = TEGRA_UART_TX_DMA;
+ tup->tx_bytes_requested = tup->tx_bytes;
+ tup->tx_cookie = dmaengine_submit(tup->tx_dma_desc);
+ dma_async_issue_pending(tup->tx_dma_chan);
+ return 0;
+}
+
+static void tegra_uart_start_next_tx(struct tegra_uart_port *tup)
+{
+ unsigned long tail;
+ unsigned long count;
+ struct circ_buf *xmit = &tup->uport.state->xmit;
+
+ tail = (unsigned long)&xmit->buf[xmit->tail];
+ count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+ if (!count)
+ return;
+
+ if (count < TEGRA_UART_MIN_DMA)
+ tegra_uart_start_pio_tx(tup, count);
+ else if (BYTES_TO_ALIGN(tail) > 0)
+ tegra_uart_start_pio_tx(tup, BYTES_TO_ALIGN(tail));
+ else
+ tegra_uart_start_tx_dma(tup, count);
+}
+
+/* Called by serial core driver with u->lock taken. */
+static void tegra_uart_start_tx(struct uart_port *u)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+ struct circ_buf *xmit = &u->state->xmit;
+
+ if (!uart_circ_empty(xmit) && !tup->tx_in_progress)
+ tegra_uart_start_next_tx(tup);
+}
+
+static unsigned int tegra_uart_tx_empty(struct uart_port *u)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+ unsigned int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&u->lock, flags);
+ if (!tup->tx_in_progress) {
+ unsigned long lsr = tegra_uart_read(tup, UART_LSR);
+ if ((lsr & TX_EMPTY_STATUS) == TX_EMPTY_STATUS)
+ ret = TIOCSER_TEMT;
+ }
+ spin_unlock_irqrestore(&u->lock, flags);
+ return ret;
+}
+
+static void tegra_uart_stop_tx(struct uart_port *u)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+ struct circ_buf *xmit = &tup->uport.state->xmit;
+ struct dma_tx_state state;
+ int count;
+
+ dmaengine_terminate_all(tup->tx_dma_chan);
+ dmaengine_tx_status(tup->tx_dma_chan, tup->tx_cookie, &state);
+ count = tup->tx_bytes_requested - state.residue;
+ async_tx_ack(tup->tx_dma_desc);
+ xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
+ tup->tx_in_progress = 0;
+ return;
+}
+
+static void tegra_uart_handle_tx_pio(struct tegra_uart_port *tup)
+{
+ struct circ_buf *xmit = &tup->uport.state->xmit;
+
+ tegra_uart_fill_tx_fifo(tup, tup->tx_bytes);
+ tup->tx_in_progress = 0;
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&tup->uport);
+ tegra_uart_start_next_tx(tup);
+ return;
+}
+
+static void tegra_uart_handle_rx_pio(struct tegra_uart_port *tup,
+ struct tty_struct *tty)
+{
+ do {
+ char flag = TTY_NORMAL;
+ unsigned long lsr = 0;
+ unsigned char ch;
+
+ lsr = tegra_uart_read(tup, UART_LSR);
+ if (!(lsr & UART_LSR_DR))
+ break;
+
+ flag = tegra_uart_decode_rx_error(tup, lsr);
+ ch = (unsigned char) tegra_uart_read(tup, UART_RX);
+ tup->uport.icount.rx++;
+
+ if (!uart_handle_sysrq_char(&tup->uport, ch) && tty)
+ tty_insert_flip_char(tty, ch, flag);
+ } while (1);
+
+ return;
+}
+
+static void tegra_uart_copy_rx_to_tty(struct tegra_uart_port *tup,
+ struct tty_struct *tty, int count)
+{
+ int copied;
+
+ tup->uport.icount.rx += count;
+ if (!tty) {
+ dev_err(tup->uport.dev, "No tty port\n");
+ return;
+ }
+ dma_sync_single_for_cpu(tup->uport.dev, tup->rx_dma_buf_phys,
+ TEGRA_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
+ copied = tty_insert_flip_string(tty,
+ ((unsigned char *)(tup->rx_dma_buf_virt)), count);
+ if (copied != count) {
+ WARN_ON(1);
+ dev_err(tup->uport.dev, "RxData copy to tty layer failed\n");
+ }
+ dma_sync_single_for_device(tup->uport.dev, tup->rx_dma_buf_phys,
+ TEGRA_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
+}
+
+static void tegra_uart_rx_dma_complete(void *args)
+{
+ struct tegra_uart_port *tup = args;
+ struct uart_port *u = &tup->uport;
+ int count = tup->rx_bytes_requested;
+ struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port);
+ unsigned long flags;
+
+ async_tx_ack(tup->rx_dma_desc);
+ spin_lock_irqsave(&u->lock, flags);
+
+ /* Deactivate flow control to stop sender */
+ if (tup->rts_active)
+ set_rts(tup, false);
+
+ /* If we are here, DMA is stopped */
+ if (count)
+ tegra_uart_copy_rx_to_tty(tup, tty, count);
+
+ tegra_uart_handle_rx_pio(tup, tty);
+ if (tty) {
+ tty_flip_buffer_push(tty);
+ tty_kref_put(tty);
+ }
+ tegra_uart_start_rx_dma(tup);
+
+ /* Activate flow control to start transfer */
+ if (tup->rts_active)
+ set_rts(tup, true);
+
+ spin_unlock_irqrestore(&u->lock, flags);
+}
+
+static void tegra_uart_handle_rx_dma(struct tegra_uart_port *tup)
+{
+ struct dma_tx_state state;
+ struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port);
+ int count;
+
+ /* Deactivate flow control to stop sender */
+ if (tup->rts_active)
+ set_rts(tup, false);
+
+ dmaengine_terminate_all(tup->rx_dma_chan);
+ dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state);
+ count = tup->rx_bytes_requested - state.residue;
+
+ /* If we are here, DMA is stopped */
+ if (count)
+ tegra_uart_copy_rx_to_tty(tup, tty, count);
+
+ tegra_uart_handle_rx_pio(tup, tty);
+ if (tty) {
+ tty_flip_buffer_push(tty);
+ tty_kref_put(tty);
+ }
+ tegra_uart_start_rx_dma(tup);
+
+ if (tup->rts_active)
+ set_rts(tup, true);
+}
+
+static int tegra_uart_start_rx_dma(struct tegra_uart_port *tup)
+{
+ unsigned int count = TEGRA_UART_RX_DMA_BUFFER_SIZE;
+
+ tup->rx_dma_desc = dmaengine_prep_slave_single(tup->rx_dma_chan,
+ tup->rx_dma_buf_phys, count, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!tup->rx_dma_desc) {
+ dev_err(tup->uport.dev, "Not able to get desc for Rx\n");
+ return -EIO;
+ }
+
+ tup->rx_dma_desc->callback = tegra_uart_rx_dma_complete;
+ tup->rx_dma_desc->callback_param = tup;
+ dma_sync_single_for_device(tup->uport.dev, tup->rx_dma_buf_phys,
+ count, DMA_TO_DEVICE);
+ tup->rx_bytes_requested = count;
+ tup->rx_cookie = dmaengine_submit(tup->rx_dma_desc);
+ dma_async_issue_pending(tup->rx_dma_chan);
+ return 0;
+}
+
+static void tegra_uart_handle_modem_signal_change(struct uart_port *u)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+ unsigned long msr;
+
+ msr = tegra_uart_read(tup, UART_MSR);
+ if (!(msr & UART_MSR_ANY_DELTA))
+ return;
+
+ if (msr & UART_MSR_TERI)
+ tup->uport.icount.rng++;
+ if (msr & UART_MSR_DDSR)
+ tup->uport.icount.dsr++;
+ /* We may only get DDCD when HW init and reset */
+ if (msr & UART_MSR_DDCD)
+ uart_handle_dcd_change(&tup->uport, msr & UART_MSR_DCD);
+ /* Will start/stop_tx accordingly */
+ if (msr & UART_MSR_DCTS)
+ uart_handle_cts_change(&tup->uport, msr & UART_MSR_CTS);
+ return;
+}
+
+static irqreturn_t tegra_uart_isr(int irq, void *data)
+{
+ struct tegra_uart_port *tup = data;
+ struct uart_port *u = &tup->uport;
+ unsigned long iir;
+ unsigned long ier;
+ bool is_rx_int = false;
+ unsigned long flags;
+
+ spin_lock_irqsave(&u->lock, flags);
+ while (1) {
+ iir = tegra_uart_read(tup, UART_IIR);
+ if (iir & UART_IIR_NO_INT) {
+ if (is_rx_int) {
+ tegra_uart_handle_rx_dma(tup);
+ if (tup->rx_in_progress) {
+ ier = tup->ier_shadow;
+ ier |= (UART_IER_RLSI | UART_IER_RTOIE |
+ TEGRA_UART_IER_EORD);
+ tup->ier_shadow = ier;
+ tegra_uart_write(tup, ier, UART_IER);
+ }
+ }
+ spin_unlock_irqrestore(&u->lock, flags);
+ return IRQ_HANDLED;
+ }
+
+ switch ((iir >> 1) & 0x7) {
+ case 0: /* Modem signal change interrupt */
+ tegra_uart_handle_modem_signal_change(u);
+ break;
+
+ case 1: /* Transmit interrupt only triggered when using PIO */
+ tup->ier_shadow &= ~UART_IER_THRI;
+ tegra_uart_write(tup, tup->ier_shadow, UART_IER);
+ tegra_uart_handle_tx_pio(tup);
+ break;
+
+ case 4: /* End of data */
+ case 6: /* Rx timeout */
+ case 2: /* Receive */
+ if (!is_rx_int) {
+ is_rx_int = true;
+ /* Disable Rx interrupts */
+ ier = tup->ier_shadow;
+ ier |= UART_IER_RDI;
+ tegra_uart_write(tup, ier, UART_IER);
+ ier &= ~(UART_IER_RDI | UART_IER_RLSI |
+ UART_IER_RTOIE | TEGRA_UART_IER_EORD);
+ tup->ier_shadow = ier;
+ tegra_uart_write(tup, ier, UART_IER);
+ }
+ break;
+
+ case 3: /* Receive error */
+ tegra_uart_decode_rx_error(tup,
+ tegra_uart_read(tup, UART_LSR));
+ break;
+
+ case 5: /* break nothing to handle */
+ case 7: /* break nothing to handle */
+ break;
+ }
+ }
+}
+
+static void tegra_uart_stop_rx(struct uart_port *u)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+ struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port);
+ struct dma_tx_state state;
+ unsigned long ier;
+ int count;
+
+ if (tup->rts_active)
+ set_rts(tup, false);
+
+ if (!tup->rx_in_progress)
+ return;
+
+ tegra_uart_wait_sym_time(tup, 1); /* wait a character interval */
+
+ ier = tup->ier_shadow;
+ ier &= ~(UART_IER_RDI | UART_IER_RLSI | UART_IER_RTOIE |
+ TEGRA_UART_IER_EORD);
+ tup->ier_shadow = ier;
+ tegra_uart_write(tup, ier, UART_IER);
+ tup->rx_in_progress = 0;
+ if (tup->rx_dma_chan) {
+ dmaengine_terminate_all(tup->rx_dma_chan);
+ dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state);
+ async_tx_ack(tup->rx_dma_desc);
+ count = tup->rx_bytes_requested - state.residue;
+ tegra_uart_copy_rx_to_tty(tup, tty, count);
+ tegra_uart_handle_rx_pio(tup, tty);
+ } else {
+ tegra_uart_handle_rx_pio(tup, tty);
+ }
+ if (tty) {
+ tty_flip_buffer_push(tty);
+ tty_kref_put(tty);
+ }
+ return;
+}
+
+static void tegra_uart_hw_deinit(struct tegra_uart_port *tup)
+{
+ unsigned long flags;
+ unsigned long char_time = DIV_ROUND_UP(10000000, tup->current_baud);
+ unsigned long fifo_empty_time = tup->uport.fifosize * char_time;
+ unsigned long wait_time;
+ unsigned long lsr;
+ unsigned long msr;
+ unsigned long mcr;
+
+ /* Disable interrupts */
+ tegra_uart_write(tup, 0, UART_IER);
+
+ lsr = tegra_uart_read(tup, UART_LSR);
+ if ((lsr & UART_LSR_TEMT) != UART_LSR_TEMT) {
+ msr = tegra_uart_read(tup, UART_MSR);
+ mcr = tegra_uart_read(tup, UART_MCR);
+ if ((mcr & TEGRA_UART_MCR_CTS_EN) && (msr & UART_MSR_CTS))
+ dev_err(tup->uport.dev,
+ "Tx Fifo not empty, CTS disabled, waiting\n");
+
+ /* Wait for Tx fifo to be empty */
+ while ((lsr & UART_LSR_TEMT) != UART_LSR_TEMT) {
+ wait_time = min(fifo_empty_time, 100lu);
+ udelay(wait_time);
+ fifo_empty_time -= wait_time;
+ if (!fifo_empty_time) {
+ msr = tegra_uart_read(tup, UART_MSR);
+ mcr = tegra_uart_read(tup, UART_MCR);
+ if ((mcr & TEGRA_UART_MCR_CTS_EN) &&
+ (msr & UART_MSR_CTS))
+ dev_err(tup->uport.dev,
+ "Slave not ready\n");
+ break;
+ }
+ lsr = tegra_uart_read(tup, UART_LSR);
+ }
+ }
+
+ spin_lock_irqsave(&tup->uport.lock, flags);
+ /* Reset the Rx and Tx FIFOs */
+ tegra_uart_fifo_reset(tup, UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR);
+ tup->current_baud = 0;
+ spin_unlock_irqrestore(&tup->uport.lock, flags);
+
+ clk_disable_unprepare(tup->uart_clk);
+}
+
+static int tegra_uart_hw_init(struct tegra_uart_port *tup)
+{
+ int ret;
+
+ tup->fcr_shadow = 0;
+ tup->mcr_shadow = 0;
+ tup->lcr_shadow = 0;
+ tup->ier_shadow = 0;
+ tup->current_baud = 0;
+
+ clk_prepare_enable(tup->uart_clk);
+
+ /* Reset the UART controller to clear all previous status.*/
+ tegra_periph_reset_assert(tup->uart_clk);
+ udelay(10);
+ tegra_periph_reset_deassert(tup->uart_clk);
+
+ tup->rx_in_progress = 0;
+ tup->tx_in_progress = 0;
+
+ /*
+ * Set the trigger level
+ *
+ * For PIO mode:
+ *
+ * For receive, this will interrupt the CPU after that many number of
+ * bytes are received, for the remaining bytes the receive timeout
+ * interrupt is received. Rx high watermark is set to 4.
+ *
+ * For transmit, if the trasnmit interrupt is enabled, this will
+ * interrupt the CPU when the number of entries in the FIFO reaches the
+ * low watermark. Tx low watermark is set to 16 bytes.
+ *
+ * For DMA mode:
+ *
+ * Set the Tx trigger to 16. This should match the DMA burst size that
+ * programmed in the DMA registers.
+ */
+ tup->fcr_shadow = UART_FCR_ENABLE_FIFO;
+ tup->fcr_shadow |= UART_FCR_R_TRIG_01;
+ tup->fcr_shadow |= TEGRA_UART_TX_TRIG_16B;
+ tegra_uart_write(tup, tup->fcr_shadow, UART_FCR);
+
+ /*
+ * Initialize the UART with default configuration
+ * (115200, N, 8, 1) so that the receive DMA buffer may be
+ * enqueued
+ */
+ tup->lcr_shadow = TEGRA_UART_DEFAULT_LSR;
+ tegra_set_baudrate(tup, TEGRA_UART_DEFAULT_BAUD);
+ tup->fcr_shadow |= UART_FCR_DMA_SELECT;
+ tegra_uart_write(tup, tup->fcr_shadow, UART_FCR);
+
+ ret = tegra_uart_start_rx_dma(tup);
+ if (ret < 0) {
+ dev_err(tup->uport.dev, "Not able to start Rx DMA\n");
+ return ret;
+ }
+ tup->rx_in_progress = 1;
+
+ /*
+ * Enable IE_RXS for the receive status interrupts like line errros.
+ * Enable IE_RX_TIMEOUT to get the bytes which cannot be DMA'd.
+ *
+ * If using DMA mode, enable EORD instead of receive interrupt which
+ * will interrupt after the UART is done with the receive instead of
+ * the interrupt when the FIFO "threshold" is reached.
+ *
+ * EORD is different interrupt than RX_TIMEOUT - RX_TIMEOUT occurs when
+ * the DATA is sitting in the FIFO and couldn't be transferred to the
+ * DMA as the DMA size alignment(4 bytes) is not met. EORD will be
+ * triggered when there is a pause of the incomming data stream for 4
+ * characters long.
+ *
+ * For pauses in the data which is not aligned to 4 bytes, we get
+ * both the EORD as well as RX_TIMEOUT - SW sees RX_TIMEOUT first
+ * then the EORD.
+ */
+ tup->ier_shadow = UART_IER_RLSI | UART_IER_RTOIE | TEGRA_UART_IER_EORD;
+ tegra_uart_write(tup, tup->ier_shadow, UART_IER);
+ return 0;
+}
+
+static int tegra_uart_dma_channel_allocate(struct tegra_uart_port *tup,
+ bool dma_to_memory)
+{
+ struct dma_chan *dma_chan;
+ unsigned char *dma_buf;
+ dma_addr_t dma_phys;
+ int ret;
+ struct dma_slave_config dma_sconfig;
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ dma_chan = dma_request_channel(mask, NULL, NULL);
+ if (!dma_chan) {
+ dev_err(tup->uport.dev,
+ "Dma channel is not available, will try later\n");
+ return -EPROBE_DEFER;
+ }
+
+ if (dma_to_memory) {
+ dma_buf = dma_alloc_coherent(tup->uport.dev,
+ TEGRA_UART_RX_DMA_BUFFER_SIZE,
+ &dma_phys, GFP_KERNEL);
+ if (!dma_buf) {
+ dev_err(tup->uport.dev,
+ "Not able to allocate the dma buffer\n");
+ dma_release_channel(dma_chan);
+ return -ENOMEM;
+ }
+ } else {
+ dma_phys = dma_map_single(tup->uport.dev,
+ tup->uport.state->xmit.buf, UART_XMIT_SIZE,
+ DMA_TO_DEVICE);
+ dma_buf = tup->uport.state->xmit.buf;
+ }
+
+ dma_sconfig.slave_id = tup->dma_req_sel;
+ if (dma_to_memory) {
+ dma_sconfig.src_addr = tup->uport.mapbase;
+ dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_sconfig.src_maxburst = 4;
+ } else {
+ dma_sconfig.dst_addr = tup->uport.mapbase;
+ dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_sconfig.dst_maxburst = 16;
+ }
+
+ ret = dmaengine_slave_config(dma_chan, &dma_sconfig);
+ if (ret < 0) {
+ dev_err(tup->uport.dev,
+ "Dma slave config failed, err = %d\n", ret);
+ goto scrub;
+ }
+
+ if (dma_to_memory) {
+ tup->rx_dma_chan = dma_chan;
+ tup->rx_dma_buf_virt = dma_buf;
+ tup->rx_dma_buf_phys = dma_phys;
+ } else {
+ tup->tx_dma_chan = dma_chan;
+ tup->tx_dma_buf_virt = dma_buf;
+ tup->tx_dma_buf_phys = dma_phys;
+ }
+ return 0;
+
+scrub:
+ dma_release_channel(dma_chan);
+ return ret;
+}
+
+static void tegra_uart_dma_channel_free(struct tegra_uart_port *tup,
+ bool dma_to_memory)
+{
+ struct dma_chan *dma_chan;
+
+ if (dma_to_memory) {
+ dma_free_coherent(tup->uport.dev, TEGRA_UART_RX_DMA_BUFFER_SIZE,
+ tup->rx_dma_buf_virt, tup->rx_dma_buf_phys);
+ dma_chan = tup->rx_dma_chan;
+ tup->rx_dma_chan = NULL;
+ tup->rx_dma_buf_phys = 0;
+ tup->rx_dma_buf_virt = NULL;
+ } else {
+ dma_unmap_single(tup->uport.dev, tup->tx_dma_buf_phys,
+ UART_XMIT_SIZE, DMA_TO_DEVICE);
+ dma_chan = tup->tx_dma_chan;
+ tup->tx_dma_chan = NULL;
+ tup->tx_dma_buf_phys = 0;
+ tup->tx_dma_buf_virt = NULL;
+ }
+ dma_release_channel(dma_chan);
+}
+
+static int tegra_uart_startup(struct uart_port *u)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+ int ret;
+
+ ret = tegra_uart_dma_channel_allocate(tup, false);
+ if (ret < 0) {
+ dev_err(u->dev, "Tx Dma allocation failed, err = %d\n", ret);
+ return ret;
+ }
+
+ ret = tegra_uart_dma_channel_allocate(tup, true);
+ if (ret < 0) {
+ dev_err(u->dev, "Rx Dma allocation failed, err = %d\n", ret);
+ goto fail_rx_dma;
+ }
+
+ ret = tegra_uart_hw_init(tup);
+ if (ret < 0) {
+ dev_err(u->dev, "Uart HW init failed, err = %d\n", ret);
+ goto fail_hw_init;
+ }
+
+ ret = request_irq(u->irq, tegra_uart_isr, IRQF_DISABLED,
+ dev_name(u->dev), tup);
+ if (ret < 0) {
+ dev_err(u->dev, "Failed to register ISR for IRQ %d\n", u->irq);
+ goto fail_hw_init;
+ }
+ return 0;
+
+fail_hw_init:
+ tegra_uart_dma_channel_free(tup, true);
+fail_rx_dma:
+ tegra_uart_dma_channel_free(tup, false);
+ return ret;
+}
+
+static void tegra_uart_shutdown(struct uart_port *u)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+
+ tegra_uart_hw_deinit(tup);
+
+ tup->rx_in_progress = 0;
+ tup->tx_in_progress = 0;
+
+ tegra_uart_dma_channel_free(tup, true);
+ tegra_uart_dma_channel_free(tup, false);
+ free_irq(u->irq, tup);
+}
+
+static void tegra_uart_enable_ms(struct uart_port *u)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+
+ if (tup->enable_modem_interrupt) {
+ tup->ier_shadow |= UART_IER_MSI;
+ tegra_uart_write(tup, tup->ier_shadow, UART_IER);
+ }
+}
+
+static void tegra_uart_set_termios(struct uart_port *u,
+ struct ktermios *termios, struct ktermios *oldtermios)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+ unsigned int baud;
+ unsigned long flags;
+ unsigned int lcr;
+ int symb_bit = 1;
+ struct clk *parent_clk = clk_get_parent(tup->uart_clk);
+ unsigned long parent_clk_rate = clk_get_rate(parent_clk);
+ int max_divider = (tup->cdata->support_clk_src_div) ? 0x7FFF : 0xFFFF;
+
+ max_divider *= 16;
+ spin_lock_irqsave(&u->lock, flags);
+
+ /* Changing configuration, it is safe to stop any rx now */
+ if (tup->rts_active)
+ set_rts(tup, false);
+
+ /* Clear all interrupts as configuration is going to be change */
+ tegra_uart_write(tup, tup->ier_shadow | UART_IER_RDI, UART_IER);
+ tegra_uart_read(tup, UART_IER);
+ tegra_uart_write(tup, 0, UART_IER);
+ tegra_uart_read(tup, UART_IER);
+
+ /* Parity */
+ lcr = tup->lcr_shadow;
+ lcr &= ~UART_LCR_PARITY;
+
+ /* CMSPAR isn't supported by this driver */
+ termios->c_cflag &= ~CMSPAR;
+
+ if ((termios->c_cflag & PARENB) == PARENB) {
+ symb_bit++;
+ if (termios->c_cflag & PARODD) {
+ lcr |= UART_LCR_PARITY;
+ lcr &= ~UART_LCR_EPAR;
+ lcr &= ~UART_LCR_SPAR;
+ } else {
+ lcr |= UART_LCR_PARITY;
+ lcr |= UART_LCR_EPAR;
+ lcr &= ~UART_LCR_SPAR;
+ }
+ }
+
+ lcr &= ~UART_LCR_WLEN8;
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ lcr |= UART_LCR_WLEN5;
+ symb_bit += 5;
+ break;
+ case CS6:
+ lcr |= UART_LCR_WLEN6;
+ symb_bit += 6;
+ break;
+ case CS7:
+ lcr |= UART_LCR_WLEN7;
+ symb_bit += 7;
+ break;
+ default:
+ lcr |= UART_LCR_WLEN8;
+ symb_bit += 8;
+ break;
+ }
+
+ /* Stop bits */
+ if (termios->c_cflag & CSTOPB) {
+ lcr |= UART_LCR_STOP;
+ symb_bit += 2;
+ } else {
+ lcr &= ~UART_LCR_STOP;
+ symb_bit++;
+ }
+
+ tegra_uart_write(tup, lcr, UART_LCR);
+ tup->lcr_shadow = lcr;
+ tup->symb_bit = symb_bit;
+
+ /* Baud rate. */
+ baud = uart_get_baud_rate(u, termios, oldtermios,
+ parent_clk_rate/max_divider,
+ parent_clk_rate/16);
+ spin_unlock_irqrestore(&u->lock, flags);
+ tegra_set_baudrate(tup, baud);
+ if (tty_termios_baud_rate(termios))
+ tty_termios_encode_baud_rate(termios, baud, baud);
+ spin_lock_irqsave(&u->lock, flags);
+
+ /* Flow control */
+ if (termios->c_cflag & CRTSCTS) {
+ tup->mcr_shadow |= TEGRA_UART_MCR_CTS_EN;
+ tup->mcr_shadow &= ~TEGRA_UART_MCR_RTS_EN;
+ tegra_uart_write(tup, tup->mcr_shadow, UART_MCR);
+ /* if top layer has asked to set rts active then do so here */
+ if (tup->rts_active)
+ set_rts(tup, true);
+ } else {
+ tup->mcr_shadow &= ~TEGRA_UART_MCR_CTS_EN;
+ tup->mcr_shadow &= ~TEGRA_UART_MCR_RTS_EN;
+ tegra_uart_write(tup, tup->mcr_shadow, UART_MCR);
+ }
+
+ /* update the port timeout based on new settings */
+ uart_update_timeout(u, termios->c_cflag, baud);
+
+ /* Make sure all write has completed */
+ tegra_uart_read(tup, UART_IER);
+
+ /* Reenable interrupt */
+ tegra_uart_write(tup, tup->ier_shadow, UART_IER);
+ tegra_uart_read(tup, UART_IER);
+
+ spin_unlock_irqrestore(&u->lock, flags);
+ return;
+}
+
+/*
+ * Flush any TX data submitted for DMA and PIO. Called when the
+ * TX circular buffer is reset.
+ */
+static void tegra_uart_flush_buffer(struct uart_port *u)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+
+ tup->tx_bytes = 0;
+ if (tup->tx_dma_chan)
+ dmaengine_terminate_all(tup->tx_dma_chan);
+ return;
+}
+
+static const char *tegra_uart_type(struct uart_port *u)
+{
+ return TEGRA_UART_TYPE;
+}
+
+static struct uart_ops tegra_uart_ops = {
+ .tx_empty = tegra_uart_tx_empty,
+ .set_mctrl = tegra_uart_set_mctrl,
+ .get_mctrl = tegra_uart_get_mctrl,
+ .stop_tx = tegra_uart_stop_tx,
+ .start_tx = tegra_uart_start_tx,
+ .stop_rx = tegra_uart_stop_rx,
+ .flush_buffer = tegra_uart_flush_buffer,
+ .enable_ms = tegra_uart_enable_ms,
+ .break_ctl = tegra_uart_break_ctl,
+ .startup = tegra_uart_startup,
+ .shutdown = tegra_uart_shutdown,
+ .set_termios = tegra_uart_set_termios,
+ .type = tegra_uart_type,
+ .request_port = tegra_uart_request_port,
+ .release_port = tegra_uart_release_port,
+};
+
+static struct uart_driver tegra_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = "tegra_hsuart",
+ .dev_name = "ttyTHS",
+ .cons = 0,
+ .nr = TEGRA_UART_MAXIMUM,
+};
+
+static int tegra_uart_parse_dt(struct platform_device *pdev,
+ struct tegra_uart_port *tup)
+{
+ struct device_node *np = pdev->dev.of_node;
+ u32 of_dma[2];
+ int port;
+
+ if (of_property_read_u32_array(np, "nvidia,dma-request-selector",
+ of_dma, 2) >= 0) {
+ tup->dma_req_sel = of_dma[1];
+ } else {
+ dev_err(&pdev->dev, "missing dma requestor in device tree\n");
+ return -EINVAL;
+ }
+
+ port = of_alias_get_id(np, "serial");
+ if (port < 0) {
+ dev_err(&pdev->dev, "failed to get alias id, errno %d\n", port);
+ return port;
+ }
+ tup->uport.line = port;
+
+ tup->enable_modem_interrupt = of_property_read_bool(np,
+ "nvidia,enable-modem-interrupt");
+ return 0;
+}
+
+struct tegra_uart_chip_data tegra20_uart_chip_data = {
+ .tx_fifo_full_status = false,
+ .allow_txfifo_reset_fifo_mode = true,
+ .support_clk_src_div = false,
+};
+
+struct tegra_uart_chip_data tegra30_uart_chip_data = {
+ .tx_fifo_full_status = true,
+ .allow_txfifo_reset_fifo_mode = false,
+ .support_clk_src_div = true,
+};
+
+static struct of_device_id tegra_uart_of_match[] = {
+ {
+ .compatible = "nvidia,tegra30-hsuart",
+ .data = &tegra30_uart_chip_data,
+ }, {
+ .compatible = "nvidia,tegra20-hsuart",
+ .data = &tegra20_uart_chip_data,
+ }, {
+ },
+};
+MODULE_DEVICE_TABLE(of, tegra_uart_of_match);
+
+static int tegra_uart_probe(struct platform_device *pdev)
+{
+ struct tegra_uart_port *tup;
+ struct uart_port *u;
+ struct resource *resource;
+ int ret;
+ const struct tegra_uart_chip_data *cdata;
+ const struct of_device_id *match;
+
+ match = of_match_device(of_match_ptr(tegra_uart_of_match),
+ &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ return -ENODEV;
+ }
+ cdata = match->data;
+
+ tup = devm_kzalloc(&pdev->dev, sizeof(*tup), GFP_KERNEL);
+ if (!tup) {
+ dev_err(&pdev->dev, "Failed to allocate memory for tup\n");
+ return -ENOMEM;
+ }
+
+ ret = tegra_uart_parse_dt(pdev, tup);
+ if (ret < 0)
+ return ret;
+
+ u = &tup->uport;
+ u->dev = &pdev->dev;
+ u->ops = &tegra_uart_ops;
+ u->type = PORT_TEGRA;
+ u->fifosize = 32;
+ tup->cdata = cdata;
+
+ platform_set_drvdata(pdev, tup);
+ resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!resource) {
+ dev_err(&pdev->dev, "No IO memory resource\n");
+ return -ENODEV;
+ }
+
+ u->mapbase = resource->start;
+ u->membase = devm_request_and_ioremap(&pdev->dev, resource);
+ if (!u->membase) {
+ dev_err(&pdev->dev, "memregion/iomap address req failed\n");
+ return -EADDRNOTAVAIL;
+ }
+
+ tup->uart_clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(tup->uart_clk)) {
+ dev_err(&pdev->dev, "Couldn't get the clock\n");
+ return PTR_ERR(tup->uart_clk);
+ }
+
+ u->iotype = UPIO_MEM32;
+ u->irq = platform_get_irq(pdev, 0);
+ u->regshift = 2;
+ ret = uart_add_one_port(&tegra_uart_driver, u);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to add uart port, err %d\n", ret);
+ return ret;
+ }
+ return ret;
+}
+
+static int tegra_uart_remove(struct platform_device *pdev)
+{
+ struct tegra_uart_port *tup = platform_get_drvdata(pdev);
+ struct uart_port *u = &tup->uport;
+
+ uart_remove_one_port(&tegra_uart_driver, u);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tegra_uart_suspend(struct device *dev)
+{
+ struct tegra_uart_port *tup = dev_get_drvdata(dev);
+ struct uart_port *u = &tup->uport;
+
+ return uart_suspend_port(&tegra_uart_driver, u);
+}
+
+static int tegra_uart_resume(struct device *dev)
+{
+ struct tegra_uart_port *tup = dev_get_drvdata(dev);
+ struct uart_port *u = &tup->uport;
+
+ return uart_resume_port(&tegra_uart_driver, u);
+}
+#endif
+
+static const struct dev_pm_ops tegra_uart_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(tegra_uart_suspend, tegra_uart_resume)
+};
+
+static struct platform_driver tegra_uart_platform_driver = {
+ .probe = tegra_uart_probe,
+ .remove = tegra_uart_remove,
+ .driver = {
+ .name = "serial-tegra",
+ .of_match_table = of_match_ptr(tegra_uart_of_match),
+ .pm = &tegra_uart_pm_ops,
+ },
+};
+
+static int __init tegra_uart_init(void)
+{
+ int ret;
+
+ ret = uart_register_driver(&tegra_uart_driver);
+ if (ret < 0) {
+ pr_err("Could not register %s driver\n",
+ tegra_uart_driver.driver_name);
+ return ret;
+ }
+
+ ret = platform_driver_register(&tegra_uart_platform_driver);
+ if (ret < 0) {
+ pr_err("Uart platfrom driver register failed, e = %d\n", ret);
+ uart_unregister_driver(&tegra_uart_driver);
+ return ret;
+ }
+ return 0;
+}
+
+static void __exit tegra_uart_exit(void)
+{
+ pr_info("Unloading tegra uart driver\n");
+ platform_driver_unregister(&tegra_uart_platform_driver);
+ uart_unregister_driver(&tegra_uart_driver);
+}
+
+module_init(tegra_uart_init);
+module_exit(tegra_uart_exit);
+
+MODULE_ALIAS("platform:serial-tegra");
+MODULE_DESCRIPTION("High speed UART driver for tegra chipset");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
--
1.7.1.1
^ permalink raw reply related
* [PATCH v3 RESEND] mxs: uart: allow setting RTS from software
From: Steffen Trumtrar @ 2013-01-08 9:28 UTC (permalink / raw)
To: linux-arm-kernel
Cc: linux-serial, Greg Kroah-Hartman, Steffen Trumtrar, stable
With the patch "serial: mxs-auart: fix the wrong RTS hardware flow control" the
mainline mxs-uart driver now sets RTSEN only when hardware flow control is
enabled via software. It is not possible any longer to set RTS manually via
software. However, the manual modification is a valid operation.
Regain the possibility to set RTS via software and only set RTSEN when hardware
flow control is explicitly enabled via settermios cflag CRTSCTS.
Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
Reviewed-by: Huang Shijie <b32955@freescale.com>
Cc: stable@vger.kernel.org
---
drivers/tty/serial/mxs-auart.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index 6db23b0..9f63f88 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -412,10 +412,12 @@ static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl)
u32 ctrl = readl(u->membase + AUART_CTRL2);
- ctrl &= ~AUART_CTRL2_RTSEN;
+ ctrl &= ~(AUART_CTRL2_RTSEN | AUART_CTRL2_RTS);
if (mctrl & TIOCM_RTS) {
if (tty_port_cts_enabled(&u->state->port))
ctrl |= AUART_CTRL2_RTSEN;
+ else
+ ctrl |= AUART_CTRL2_RTS;
}
s->ctrl = mctrl;
--
1.7.10.4
^ permalink raw reply related
* Re: [PATCH] serial: mxs-auart: Index is unsigned
From: Marek Vasut @ 2013-01-08 6:58 UTC (permalink / raw)
To: Fabio Estevam; +Cc: alan, shawn.guo, linux-serial, Fabio Estevam
In-Reply-To: <1357607466-20358-1-git-send-email-festevam@gmail.com>
Dear Fabio Estevam,
> From: Fabio Estevam <fabio.estevam@freescale.com>>
>
> Fix the following warning when building with W=1 option:
>
> drivers/tty/serial/mxs-auart.c: In function 'mxs_auart_tx_chars':
> drivers/tty/serial/mxs-auart.c:272:10: warning: comparison between signed
> and unsigned integer expressions [-Wsign-compare]
>
> Signed-off-by: Fabio Estevam <fabio.estevam@freescale.com>
Acked-by: Marek Vasut <marex@denx.de>
> ---
> drivers/tty/serial/mxs-auart.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/tty/serial/mxs-auart.c
> b/drivers/tty/serial/mxs-auart.c index 6db23b0..537e0af 100644
> --- a/drivers/tty/serial/mxs-auart.c
> +++ b/drivers/tty/serial/mxs-auart.c
> @@ -253,7 +253,7 @@ static void mxs_auart_tx_chars(struct mxs_auart_port
> *s) struct circ_buf *xmit = &s->port.state->xmit;
>
> if (auart_dma_enabled(s)) {
> - int i = 0;
> + u32 i = 0;
> int size;
> void *buffer = s->tx_dma_buf;
Best regards,
Marek Vasut
^ permalink raw reply
* [PATCH] serial: mxs-auart: Index is unsigned
From: Fabio Estevam @ 2013-01-08 1:11 UTC (permalink / raw)
To: alan; +Cc: shawn.guo, linux-serial, marex, Fabio Estevam
From: Fabio Estevam <fabio.estevam@freescale.com>>
Fix the following warning when building with W=1 option:
drivers/tty/serial/mxs-auart.c: In function 'mxs_auart_tx_chars':
drivers/tty/serial/mxs-auart.c:272:10: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
Signed-off-by: Fabio Estevam <fabio.estevam@freescale.com>
---
drivers/tty/serial/mxs-auart.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index 6db23b0..537e0af 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -253,7 +253,7 @@ static void mxs_auart_tx_chars(struct mxs_auart_port *s)
struct circ_buf *xmit = &s->port.state->xmit;
if (auart_dma_enabled(s)) {
- int i = 0;
+ u32 i = 0;
int size;
void *buffer = s->tx_dma_buf;
--
1.7.9.5
^ permalink raw reply related
* Re: [PATCH v3] mxs: uart: allow setting RTS from software
From: Shawn Guo @ 2013-01-08 1:06 UTC (permalink / raw)
To: Marc Kleine-Budde
Cc: Huang Shijie, Steffen Trumtrar, Alan Cox, stable,
linux-arm-kernel, linux-serial
In-Reply-To: <50EA98CA.1060303@pengutronix.de>
On Mon, Jan 07, 2013 at 10:43:38AM +0100, Marc Kleine-Budde wrote:
> On 12/14/2012 03:27 AM, Huang Shijie wrote:
> > 于 2012年12月13日 21:27, Steffen Trumtrar 写道:
> >> With the patch "serial: mxs-auart: fix the wrong RTS hardware flow control" the
> >> mainline mxs-uart driver now sets RTSEN only when hardware flow control is
> >> enabled via software. It is not possible any longer to set RTS manually via
> >> software. However, the manual modification is a valid operation.
> >> Regain the possibility to set RTS via software and only set RTSEN when hardware
> >> flow control is explicitly enabled via settermios cflag CRTSCTS.
> >>
> >> Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
> >> ---
> >> drivers/tty/serial/mxs-auart.c | 4 +++-
> >> 1 file changed, 3 insertions(+), 1 deletion(-)
> >>
> >> diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
> >> index 6db23b0..9f63f88 100644
> >> --- a/drivers/tty/serial/mxs-auart.c
> >> +++ b/drivers/tty/serial/mxs-auart.c
> >> @@ -412,10 +412,12 @@ static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl)
> >>
> >> u32 ctrl = readl(u->membase + AUART_CTRL2);
> >>
> >> - ctrl &= ~AUART_CTRL2_RTSEN;
> >> + ctrl &= ~(AUART_CTRL2_RTSEN | AUART_CTRL2_RTS);
> >> if (mctrl & TIOCM_RTS) {
> >> if (tty_port_cts_enabled(&u->state->port))
> >> ctrl |= AUART_CTRL2_RTSEN;
> >> + else
> >> + ctrl |= AUART_CTRL2_RTS;
> >> }
> >>
> >> s->ctrl = mctrl;
> > Reviewed-by: Huang Shijie <b32955@freescale.com>
>
> This patch is not not in today's next. What's the current status? Who
> will take it?
>
I see stable@vger.kernel.org is on copy. But it's not the correct way
submit a fix for stable tree. Check out Documentation/stable_kernel_rules.txt
for the correct one.
Remember to copy Greg Kroah-Hartman <gregkh@linuxfoundation.org> who
is collecting serial patches believe.
Shawn
--
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
* [PATCH 2/2] Fix incorrect num_ports on pbn_b0_8_1152000_200 boards structure.
From: Matt Schulte @ 2013-01-07 21:17 UTC (permalink / raw)
To: alan; +Cc: linux-serial, Matt Schulte
In-Reply-To: <1357593435-3566-1-git-send-email-matts@commtech-fastcom.com>
Signed-off-by: Matt Schulte <matts@commtech-fastcom.com>
---
drivers/tty/serial/8250/8250_pci.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index 18f1d79..c8ec5fc 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -2246,7 +2246,7 @@ static struct pciserial_board pci_boards[] = {
[pbn_b0_8_1152000_200] = {
.flags = FL_BASE0,
- .num_ports = 2,
+ .num_ports = 8,
.base_baud = 1152000,
.uart_offset = 0x200,
},
--
1.7.2.5
^ permalink raw reply related
* [PATCH 1/2] Fix incorrect device ID for the Commtech 422/2-PCIe.
From: Matt Schulte @ 2013-01-07 21:17 UTC (permalink / raw)
To: alan; +Cc: linux-serial, Matt Schulte
Signed-off-by: Matt Schulte <matts@commtech-fastcom.com>
---
drivers/tty/serial/8250/8250_pci.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index 26b9dc0..18f1d79 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -1301,9 +1301,9 @@ pci_wch_ch353_setup(struct serial_private *priv,
#define PCI_VENDOR_ID_AGESTAR 0x5372
#define PCI_DEVICE_ID_AGESTAR_9375 0x6872
#define PCI_VENDOR_ID_ASIX 0x9710
-#define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0019
#define PCI_DEVICE_ID_COMMTECH_4224PCIE 0x0020
#define PCI_DEVICE_ID_COMMTECH_4228PCIE 0x0021
+#define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0022
/* Unknown vendors/cards - this should not be in linux/pci_ids.h */
--
1.7.2.5
^ permalink raw reply related
* Re: [RFC PATCH v1 25/31] ARC: [plat-arcfpga] Hooking up platform to ARC UART
From: Arnd Bergmann @ 2013-01-07 14:36 UTC (permalink / raw)
To: Vineet Gupta, linux-serial; +Cc: Grant Likely, linux-arch, linux-kernel
In-Reply-To: <50EAD5F3.7030204@synopsys.com>
On Monday 07 January 2013 19:34:35 Vineet Gupta wrote:
> On Monday 07 January 2013 07:16 PM, Arnd Bergmann wrote:
> >> static struct of_dev_auxdata arcuart_auxdata_lookup[] __initdata = {
> >> OF_DEV_AUXDATA("snps,arc-uart", UART0_BASE, "arc-uart", arc_uart_info),
> >> {}
> >> };
> > It should be enough to fill the drv->of_match_table member of the
> > platform_driver with the match table.
>
> Not sure if I understand. My concern is the #define UART0_BASE (=0xc0fc1000) which
> needs to be defined in code despite that value being present in the device tree.
> And this is needed so that framework could match the device against the driver. I
> would have thought that some device property (in device tree) could enable the
> matching with Linux name (actually DRIVER_NAME defined in the uart driver). Am I
> missing something ?
I mean you should not need the auxdata entry if the driver does everything
right, and you get rid of the platform_data.
> >> (3) After above, driver's probe routine is getting called with platform_device->id
> >> = -1 and it seems of_device_add() is doing that purposely. How do I handle that.
> > What do you need the id for?
>
> In case of multiple instances of UART, driver uses this value to index into struct
> arc_uart_port [ ]
>
> struct arc_uart_port {
> struct uart_port port;
> unsigned long baud;
> int is_emulated;
> };
>
> static struct arc_uart_port arc_uart_ports[CONFIG_SERIAL_ARC_NR_PORTS];
>
> arc_serial_probe(struct platform_device *pdev)
> uart = &arc_uart_ports[pdev->id];
> arc_uart_init_one(pdev, ..)
> if (pdev->id < 0 ...)
> return -ENOENT;
>
> Although the current driver is flawed in that it checks for -ve value after
> already indexing into the array :-(
> I'll send a patch to serial list to fix that.
(adding linux-serial to Cc)
Generally speaking, drivers should not have static or fixed-size arrays of
devices in them. What you should do instead is to allocate the arc_uart_port
at from arc_serial_probe() and remove all the references to the global
array. I'm not completely sure if that's possible with the uart_port yet,
maybe someone else can comment on that.
For the id value, have a look at the drivers/tty/serial/mxs-auart.c and how
it gets the id from the /aliases node in the device tree using
of_alias_get_id().
Arnd
^ permalink raw reply
* Re: [PATCH v3] mxs: uart: allow setting RTS from software
From: Marc Kleine-Budde @ 2013-01-07 9:43 UTC (permalink / raw)
To: Huang Shijie
Cc: Steffen Trumtrar, linux-arm-kernel, stable, linux-serial,
Alan Cox
In-Reply-To: <50CA8E8C.5060906@freescale.com>
[-- Attachment #1: Type: text/plain, Size: 1804 bytes --]
On 12/14/2012 03:27 AM, Huang Shijie wrote:
> 于 2012年12月13日 21:27, Steffen Trumtrar 写道:
>> With the patch "serial: mxs-auart: fix the wrong RTS hardware flow control" the
>> mainline mxs-uart driver now sets RTSEN only when hardware flow control is
>> enabled via software. It is not possible any longer to set RTS manually via
>> software. However, the manual modification is a valid operation.
>> Regain the possibility to set RTS via software and only set RTSEN when hardware
>> flow control is explicitly enabled via settermios cflag CRTSCTS.
>>
>> Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
>> ---
>> drivers/tty/serial/mxs-auart.c | 4 +++-
>> 1 file changed, 3 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
>> index 6db23b0..9f63f88 100644
>> --- a/drivers/tty/serial/mxs-auart.c
>> +++ b/drivers/tty/serial/mxs-auart.c
>> @@ -412,10 +412,12 @@ static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl)
>>
>> u32 ctrl = readl(u->membase + AUART_CTRL2);
>>
>> - ctrl &= ~AUART_CTRL2_RTSEN;
>> + ctrl &= ~(AUART_CTRL2_RTSEN | AUART_CTRL2_RTS);
>> if (mctrl & TIOCM_RTS) {
>> if (tty_port_cts_enabled(&u->state->port))
>> ctrl |= AUART_CTRL2_RTSEN;
>> + else
>> + ctrl |= AUART_CTRL2_RTS;
>> }
>>
>> s->ctrl = mctrl;
> Reviewed-by: Huang Shijie <b32955@freescale.com>
This patch is not not in today's next. What's the current status? Who
will take it?
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 261 bytes --]
^ permalink raw reply
* Re: [PATCH 0/5] serial: imx: Some fixes and cleanups
From: Sascha Hauer @ 2013-01-07 8:30 UTC (permalink / raw)
To: Sachin Kamat; +Cc: linux-serial, gregkh, alan, jslaby, shawn.guo, patches
In-Reply-To: <1357534506-30206-1-git-send-email-sachin.kamat@linaro.org>
On Mon, Jan 07, 2013 at 10:25:01AM +0530, Sachin Kamat wrote:
> This series build tested using imx_v4_v5_defconfig against
> linux-next tree (20130107).
>
> Sachin Kamat (5):
> serial: imx: Fix checkpatch errors related to spacing
> serial: imx: Use <linux/io.h> instead of <asm/io.h>
> serial: imx: Fix coding style issue
> serial: imx: Use pr_info instead of printk
> serial: imx: Use devm_* APIs
>
> drivers/tty/serial/imx.c | 263 ++++++++++++++++++++++------------------------
> 1 files changed, 125 insertions(+), 138 deletions(-)
Looks good.
Acked-by: Sascha Hauer <s.hauer@pengutronix.de>
Sascha
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox