summaryrefslogtreecommitdiffstats
path: root/drivers/base/soc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base/soc.c')
-rw-r--r--drivers/base/soc.c123
1 files changed, 123 insertions, 0 deletions
diff --git a/drivers/base/soc.c b/drivers/base/soc.c
new file mode 100644
index 0000000000..a481f8987b
--- /dev/null
+++ b/drivers/base/soc.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2024 Marco Felsch, Pengutronix
+/*
+ * Based on Linux drivers/base/soc.c:
+ * Copyright (C) ST-Ericsson SA 2011
+ */
+
+#include <common.h>
+#include <init.h>
+#include <of.h>
+
+#include <linux/slab.h>
+#include <linux/sys_soc.h>
+#include <linux/err.h>
+
+struct soc_device {
+ struct device dev;
+ struct soc_device_attribute *attr;
+};
+
+static struct bus_type soc_bus_type = {
+ .name = "soc",
+};
+static bool soc_bus_registered;
+
+static void soc_device_add_params(struct soc_device *soc_dev)
+{
+ struct soc_device_attribute *attr = soc_dev->attr;
+ struct device *dev = &soc_dev->dev;
+
+ if (attr->machine)
+ dev_add_param_string_fixed(dev, "machine", attr->machine);
+ if (attr->family)
+ dev_add_param_string_fixed(dev, "family", attr->family);
+ if (attr->revision)
+ dev_add_param_string_fixed(dev, "revision", attr->revision);
+ if (attr->serial_number)
+ dev_add_param_string_fixed(dev, "serial_number", attr->serial_number);
+ if (attr->soc_id)
+ dev_add_param_string_fixed(dev, "soc_id", attr->soc_id);
+}
+
+static void soc_device_get_machine(struct soc_device_attribute *soc_dev_attr)
+{
+ struct device_node *np;
+
+ if (soc_dev_attr->machine)
+ return;
+
+ np = of_find_node_by_path("/");
+ of_property_read_string(np, "model", &soc_dev_attr->machine);
+ of_node_put(np);
+}
+
+static struct soc_device_attribute *early_soc_dev_attr;
+
+struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr)
+{
+ struct soc_device *soc_dev;
+ int ret;
+
+ soc_device_get_machine(soc_dev_attr);
+
+ if (!soc_bus_registered) {
+ if (early_soc_dev_attr)
+ return ERR_PTR(-EBUSY);
+ early_soc_dev_attr = soc_dev_attr;
+ return NULL;
+ }
+
+ soc_dev = kzalloc(sizeof(*soc_dev), GFP_KERNEL);
+ if (!soc_dev) {
+ ret = -ENOMEM;
+ goto out1;
+ }
+
+ soc_dev->attr = soc_dev_attr;
+ soc_dev->dev.bus = &soc_bus_type;
+ soc_dev->dev.id = DEVICE_ID_DYNAMIC;
+
+ dev_set_name(&soc_dev->dev, "soc");
+
+ ret = device_register(&soc_dev->dev);
+ if (ret) {
+ put_device(&soc_dev->dev);
+ goto out2;
+ }
+
+ soc_device_add_params(soc_dev);
+
+ return soc_dev;
+
+out2:
+ kfree(soc_dev);
+out1:
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(soc_device_register);
+
+/* Ensure soc_dev->attr is freed after calling soc_device_unregister. */
+void soc_device_unregister(struct soc_device *soc_dev)
+{
+ device_unregister(&soc_dev->dev);
+ kfree(soc_dev);
+ early_soc_dev_attr = NULL;
+}
+EXPORT_SYMBOL_GPL(soc_device_unregister);
+
+static int __init soc_bus_register(void)
+{
+ int ret;
+
+ ret = bus_register(&soc_bus_type);
+ if (ret)
+ return ret;
+ soc_bus_registered = true;
+
+ if (early_soc_dev_attr)
+ return PTR_ERR(soc_device_register(early_soc_dev_attr));
+
+ return 0;
+}
+core_initcall(soc_bus_register);