summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorAndrey Smirnov <andrew.smirnov@gmail.com>2018-04-12 14:33:14 -0700
committerSascha Hauer <s.hauer@pengutronix.de>2018-04-17 09:21:10 +0200
commitc2ee90fb9ed90c3612301e2b86c05cfb3c008fde (patch)
treebf1200bdc97642a02a7ed89cca57be1daf57ef9c /common
parent84fc6119d3aaa33344d154428ce37800a96a39b9 (diff)
downloadbarebox-c2ee90fb9ed90c3612301e2b86c05cfb3c008fde.tar.gz
barebox-c2ee90fb9ed90c3612301e2b86c05cfb3c008fde.tar.xz
console: Add simplified 'serdev' framework from Linux kernel
Port 'serdev' UART-slave deivce framework found in recent Linux kernels (post 4.13) in order to be able to port 'serdev' slave drivers from Linux. Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'common')
-rw-r--r--common/Makefile1
-rw-r--r--common/console.c24
-rw-r--r--common/serdev.c89
3 files changed, 111 insertions, 3 deletions
diff --git a/common/Makefile b/common/Makefile
index 4e9681f203..1ff7d2370b 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_FIRMWARE) += firmware.o
obj-$(CONFIG_UBIFORMAT) += ubiformat.o
obj-$(CONFIG_BAREBOX_UPDATE_IMX_NAND_FCB) += imx-bbu-nand-fcb.o
obj-$(CONFIG_BOOT) += boot.o
+obj-$(CONFIG_SERIAL_DEV_BUS) += serdev.o
ifdef CONFIG_PASSWORD
diff --git a/common/console.c b/common/console.c
index f4c799fa54..ab3d4d397c 100644
--- a/common/console.c
+++ b/common/console.c
@@ -305,10 +305,11 @@ static ssize_t fops_write(struct cdev* dev, const void* buf, size_t count,
int console_register(struct console_device *newcdev)
{
+ struct device_node *serdev_node = console_is_serdev_node(newcdev);
struct device_d *dev = &newcdev->class_dev;
int activate = 0, ret;
- if (initialized == CONSOLE_UNINITIALIZED)
+ if (!serdev_node && initialized == CONSOLE_UNINITIALIZED)
console_init_early();
if (newcdev->devname) {
@@ -323,6 +324,17 @@ int console_register(struct console_device *newcdev)
dev->parent = newcdev->dev;
platform_device_register(dev);
+ newcdev->open_count = 0;
+
+ /*
+ * If our console device is a serdev, we skip the creation of
+ * corresponding entry in /dev as well as registration in
+ * console_list and just go straight to populating child
+ * devices.
+ */
+ if (serdev_node)
+ return of_platform_populate(serdev_node, NULL, dev);
+
if (newcdev->setbrg) {
ret = newcdev->setbrg(newcdev, CONFIG_BAUDRATE);
if (ret)
@@ -335,8 +347,6 @@ int console_register(struct console_device *newcdev)
if (newcdev->putc && !newcdev->puts)
newcdev->puts = __console_puts;
- newcdev->open_count = 0;
-
dev_add_param_string(dev, "active", console_active_set, console_active_get,
&newcdev->active_string, newcdev);
@@ -386,6 +396,14 @@ int console_unregister(struct console_device *cdev)
struct device_d *dev = &cdev->class_dev;
int status;
+ /*
+ * We don't do any sophisticated serdev device de-population
+ * and instead claim this console busy, preventing its
+ * de-initialization, 'till the very end of our execution.
+ */
+ if (console_is_serdev_node(cdev))
+ return -EBUSY;
+
devfs_remove(&cdev->devfs);
list_del(&cdev->list);
diff --git a/common/serdev.c b/common/serdev.c
new file mode 100644
index 0000000000..32743738ed
--- /dev/null
+++ b/common/serdev.c
@@ -0,0 +1,89 @@
+
+#include <common.h>
+#include <serdev.h>
+
+static void serdev_device_poller(void *context)
+{
+ struct serdev_device *serdev = context;
+ struct console_device *cdev = to_console_device(serdev);
+ unsigned char *buf = serdev->buf;
+ int ret, len;
+
+ /*
+ * Since this callback is a part of poller infrastructure we
+ * want to use _non_interruptible version of the function
+ * below to prevent recursion from happening (regular
+ * console_drain will call is_timeout, which might end up
+ * calling this function again).
+ */
+ len = console_drain_non_interruptible(cdev, serdev->fifo, buf,
+ PAGE_SIZE,
+ serdev->polling_window);
+ while (len > 0) {
+ ret = serdev->receive_buf(serdev, buf, len);
+ len -= ret;
+ buf += ret;
+ }
+
+ if (serdev->polling_interval) {
+ /*
+ * Re-schedule ourselves in 'serdev->polling_interval'
+ * nanoseconds
+ */
+ poller_call_async(&serdev->poller,
+ serdev->polling_interval,
+ serdev_device_poller,
+ serdev);
+ }
+}
+
+int serdev_device_open(struct serdev_device *serdev)
+{
+ struct console_device *cdev = to_console_device(serdev);
+ int ret;
+
+ if (!cdev->putc || !cdev->getc)
+ return -EINVAL;
+
+ if (!serdev->polling_window)
+ return -EINVAL;
+
+ serdev->buf = xzalloc(PAGE_SIZE);
+ serdev->fifo = kfifo_alloc(PAGE_SIZE);
+ if (!serdev->fifo)
+ return -ENOMEM;
+
+ ret = poller_async_register(&serdev->poller);
+ if (ret)
+ return ret;
+
+ return console_open(cdev);
+}
+
+unsigned int serdev_device_set_baudrate(struct serdev_device *serdev,
+ unsigned int speed)
+{
+ struct console_device *cdev = to_console_device(serdev);
+
+ if (console_set_baudrate(cdev, speed) < 0)
+ return 0;
+
+ return console_get_baudrate(cdev);
+}
+
+int serdev_device_write(struct serdev_device *serdev, const unsigned char *buf,
+ size_t count, unsigned long timeout)
+{
+ struct console_device *cdev = to_console_device(serdev);
+
+ while (count--)
+ cdev->putc(cdev, *buf++);
+ /*
+ * Poll Rx once right after we just send some data in case our
+ * serdev device implements command/response type of a
+ * protocol and we need to start draining input as soon as
+ * possible.
+ */
+ serdev_device_poller(serdev);
+ return 0;
+}