summaryrefslogtreecommitdiff
path: root/drivers/tty/serial/serial_base_bus.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-07-03 13:14:58 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2023-07-03 13:14:58 -0700
commit868a9fd9480785952336e5f119e1f75877c423a8 (patch)
tree87b6985e624d976da7524b90907bda8262cdc43c /drivers/tty/serial/serial_base_bus.c
parentdb9c6d1d7fcc6dc803f042bf3d29bbff91837f57 (diff)
parente534755c9412be07f579acd2947401a9f87a33c8 (diff)
Merge tag 'tty-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty/serial driver updates from Greg KH: "Here is the big set of tty/serial driver updates for 6.5-rc1. Included in here are: - tty_audit code cleanups from Jiri - more 8250 cleanups from Ilpo - samsung_tty driver bugfixes - 8250 lock port updates - usual fsl_lpuart driver updates and fixes - other small serial driver fixes and updates, full details in the shortlog All of these have been in linux-next for a while with no reported issues" * tag 'tty-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (58 commits) tty_audit: make data of tty_audit_log() const tty_audit: make tty pointers in exposed functions const tty_audit: make icanon a bool tty_audit: invert the condition in tty_audit_log() tty_audit: use kzalloc() in tty_audit_buf_alloc() tty_audit: use TASK_COMM_LEN for task comm Revert "8250: add support for ASIX devices with a FIFO bug" serial: atmel: don't enable IRQs prematurely tty: serial: Add Nuvoton ma35d1 serial driver support tty: serial: fsl_lpuart: add earlycon for imx8ulp platform tty: serial: imx: fix rs485 rx after tx selftests: tty: add selftest for tty timestamp updates tty: tty_io: update timestamps on all device nodes tty: fix hang on tty device with no_room set serial: core: fix -EPROBE_DEFER handling in init serial: 8250_omap: Use force_suspend and resume for system suspend tty: serial: samsung_tty: Use abs() to simplify some code tty: serial: samsung_tty: Fix a memory leak in s3c24xx_serial_getclk() when iterating clk tty: serial: samsung_tty: Fix a memory leak in s3c24xx_serial_getclk() in case of error serial: 8250: Apply FSL workarounds also without SERIAL_8250_CONSOLE ...
Diffstat (limited to 'drivers/tty/serial/serial_base_bus.c')
-rw-r--r--drivers/tty/serial/serial_base_bus.c205
1 files changed, 205 insertions, 0 deletions
diff --git a/drivers/tty/serial/serial_base_bus.c b/drivers/tty/serial/serial_base_bus.c
new file mode 100644
index 000000000000..6ff59c89d867
--- /dev/null
+++ b/drivers/tty/serial/serial_base_bus.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Serial base bus layer for controllers
+ *
+ * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/
+ * Author: Tony Lindgren <tony@atomide.com>
+ *
+ * The serial core bus manages the serial core controller instances.
+ */
+
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "serial_base.h"
+
+static bool serial_base_initialized;
+
+static int serial_base_match(struct device *dev, struct device_driver *drv)
+{
+ int len = strlen(drv->name);
+
+ return !strncmp(dev_name(dev), drv->name, len);
+}
+
+static struct bus_type serial_base_bus_type = {
+ .name = "serial-base",
+ .match = serial_base_match,
+};
+
+int serial_base_driver_register(struct device_driver *driver)
+{
+ driver->bus = &serial_base_bus_type;
+
+ return driver_register(driver);
+}
+
+void serial_base_driver_unregister(struct device_driver *driver)
+{
+ driver_unregister(driver);
+}
+
+static int serial_base_device_init(struct uart_port *port,
+ struct device *dev,
+ struct device *parent_dev,
+ const struct device_type *type,
+ void (*release)(struct device *dev),
+ int id)
+{
+ device_initialize(dev);
+ dev->type = type;
+ dev->parent = parent_dev;
+ dev->bus = &serial_base_bus_type;
+ dev->release = release;
+
+ if (!serial_base_initialized) {
+ dev_dbg(port->dev, "uart_add_one_port() called before arch_initcall()?\n");
+ return -EPROBE_DEFER;
+ }
+
+ return dev_set_name(dev, "%s.%s.%d", type->name, dev_name(port->dev), id);
+}
+
+static const struct device_type serial_ctrl_type = {
+ .name = "ctrl",
+};
+
+static void serial_base_ctrl_release(struct device *dev)
+{
+ struct serial_ctrl_device *ctrl_dev = to_serial_base_ctrl_device(dev);
+
+ kfree(ctrl_dev);
+}
+
+void serial_base_ctrl_device_remove(struct serial_ctrl_device *ctrl_dev)
+{
+ if (!ctrl_dev)
+ return;
+
+ device_del(&ctrl_dev->dev);
+}
+
+struct serial_ctrl_device *serial_base_ctrl_add(struct uart_port *port,
+ struct device *parent)
+{
+ struct serial_ctrl_device *ctrl_dev;
+ int err;
+
+ ctrl_dev = kzalloc(sizeof(*ctrl_dev), GFP_KERNEL);
+ if (!ctrl_dev)
+ return ERR_PTR(-ENOMEM);
+
+ err = serial_base_device_init(port, &ctrl_dev->dev,
+ parent, &serial_ctrl_type,
+ serial_base_ctrl_release,
+ port->ctrl_id);
+ if (err)
+ goto err_put_device;
+
+ err = device_add(&ctrl_dev->dev);
+ if (err)
+ goto err_put_device;
+
+ return ctrl_dev;
+
+err_put_device:
+ put_device(&ctrl_dev->dev);
+
+ return ERR_PTR(err);
+}
+
+static const struct device_type serial_port_type = {
+ .name = "port",
+};
+
+static void serial_base_port_release(struct device *dev)
+{
+ struct serial_port_device *port_dev = to_serial_base_port_device(dev);
+
+ kfree(port_dev);
+}
+
+struct serial_port_device *serial_base_port_add(struct uart_port *port,
+ struct serial_ctrl_device *ctrl_dev)
+{
+ struct serial_port_device *port_dev;
+ int err;
+
+ port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL);
+ if (!port_dev)
+ return ERR_PTR(-ENOMEM);
+
+ err = serial_base_device_init(port, &port_dev->dev,
+ &ctrl_dev->dev, &serial_port_type,
+ serial_base_port_release,
+ port->line);
+ if (err)
+ goto err_put_device;
+
+ port_dev->port = port;
+
+ err = device_add(&port_dev->dev);
+ if (err)
+ goto err_put_device;
+
+ return port_dev;
+
+err_put_device:
+ put_device(&port_dev->dev);
+
+ return ERR_PTR(err);
+}
+
+void serial_base_port_device_remove(struct serial_port_device *port_dev)
+{
+ if (!port_dev)
+ return;
+
+ device_del(&port_dev->dev);
+}
+
+static int serial_base_init(void)
+{
+ int ret;
+
+ ret = bus_register(&serial_base_bus_type);
+ if (ret)
+ return ret;
+
+ ret = serial_base_ctrl_init();
+ if (ret)
+ goto err_bus_unregister;
+
+ ret = serial_base_port_init();
+ if (ret)
+ goto err_ctrl_exit;
+
+ serial_base_initialized = true;
+
+ return 0;
+
+err_ctrl_exit:
+ serial_base_ctrl_exit();
+
+err_bus_unregister:
+ bus_unregister(&serial_base_bus_type);
+
+ return ret;
+}
+arch_initcall(serial_base_init);
+
+static void serial_base_exit(void)
+{
+ serial_base_port_exit();
+ serial_base_ctrl_exit();
+ bus_unregister(&serial_base_bus_type);
+}
+module_exit(serial_base_exit);
+
+MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
+MODULE_DESCRIPTION("Serial core bus");
+MODULE_LICENSE("GPL");