summaryrefslogtreecommitdiffstats
path: root/drivers/base/soc.c
blob: a481f8987b562a436f3008c19d54eff5a68eb628 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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);