Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/base/soc.c
26378 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Copyright (C) ST-Ericsson SA 2011
4
*
5
* Author: Lee Jones <[email protected]> for ST-Ericsson.
6
*/
7
8
#include <linux/sysfs.h>
9
#include <linux/init.h>
10
#include <linux/of.h>
11
#include <linux/stat.h>
12
#include <linux/slab.h>
13
#include <linux/idr.h>
14
#include <linux/spinlock.h>
15
#include <linux/sys_soc.h>
16
#include <linux/err.h>
17
#include <linux/glob.h>
18
19
static DEFINE_IDA(soc_ida);
20
21
/* Prototype to allow declarations of DEVICE_ATTR(<foo>) before soc_info_show */
22
static ssize_t soc_info_show(struct device *dev, struct device_attribute *attr,
23
char *buf);
24
25
struct soc_device {
26
struct device dev;
27
struct soc_device_attribute *attr;
28
int soc_dev_num;
29
};
30
31
static const struct bus_type soc_bus_type = {
32
.name = "soc",
33
};
34
static bool soc_bus_registered;
35
36
static DEVICE_ATTR(machine, 0444, soc_info_show, NULL);
37
static DEVICE_ATTR(family, 0444, soc_info_show, NULL);
38
static DEVICE_ATTR(serial_number, 0444, soc_info_show, NULL);
39
static DEVICE_ATTR(soc_id, 0444, soc_info_show, NULL);
40
static DEVICE_ATTR(revision, 0444, soc_info_show, NULL);
41
42
struct device *soc_device_to_device(struct soc_device *soc_dev)
43
{
44
return &soc_dev->dev;
45
}
46
47
static umode_t soc_attribute_mode(struct kobject *kobj,
48
struct attribute *attr,
49
int index)
50
{
51
struct device *dev = kobj_to_dev(kobj);
52
struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
53
54
if ((attr == &dev_attr_machine.attr) && soc_dev->attr->machine)
55
return attr->mode;
56
if ((attr == &dev_attr_family.attr) && soc_dev->attr->family)
57
return attr->mode;
58
if ((attr == &dev_attr_revision.attr) && soc_dev->attr->revision)
59
return attr->mode;
60
if ((attr == &dev_attr_serial_number.attr) && soc_dev->attr->serial_number)
61
return attr->mode;
62
if ((attr == &dev_attr_soc_id.attr) && soc_dev->attr->soc_id)
63
return attr->mode;
64
65
/* Unknown or unfilled attribute */
66
return 0;
67
}
68
69
static ssize_t soc_info_show(struct device *dev, struct device_attribute *attr,
70
char *buf)
71
{
72
struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
73
const char *output;
74
75
if (attr == &dev_attr_machine)
76
output = soc_dev->attr->machine;
77
else if (attr == &dev_attr_family)
78
output = soc_dev->attr->family;
79
else if (attr == &dev_attr_revision)
80
output = soc_dev->attr->revision;
81
else if (attr == &dev_attr_serial_number)
82
output = soc_dev->attr->serial_number;
83
else if (attr == &dev_attr_soc_id)
84
output = soc_dev->attr->soc_id;
85
else
86
return -EINVAL;
87
88
return sysfs_emit(buf, "%s\n", output);
89
}
90
91
static struct attribute *soc_attr[] = {
92
&dev_attr_machine.attr,
93
&dev_attr_family.attr,
94
&dev_attr_serial_number.attr,
95
&dev_attr_soc_id.attr,
96
&dev_attr_revision.attr,
97
NULL,
98
};
99
100
static const struct attribute_group soc_attr_group = {
101
.attrs = soc_attr,
102
.is_visible = soc_attribute_mode,
103
};
104
105
static void soc_release(struct device *dev)
106
{
107
struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
108
109
ida_free(&soc_ida, soc_dev->soc_dev_num);
110
kfree(soc_dev->dev.groups);
111
kfree(soc_dev);
112
}
113
114
static void soc_device_get_machine(struct soc_device_attribute *soc_dev_attr)
115
{
116
struct device_node *np;
117
118
if (soc_dev_attr->machine)
119
return;
120
121
np = of_find_node_by_path("/");
122
of_property_read_string(np, "model", &soc_dev_attr->machine);
123
of_node_put(np);
124
}
125
126
static struct soc_device_attribute *early_soc_dev_attr;
127
128
struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr)
129
{
130
struct soc_device *soc_dev;
131
const struct attribute_group **soc_attr_groups;
132
int ret;
133
134
soc_device_get_machine(soc_dev_attr);
135
136
if (!soc_bus_registered) {
137
if (early_soc_dev_attr)
138
return ERR_PTR(-EBUSY);
139
early_soc_dev_attr = soc_dev_attr;
140
return NULL;
141
}
142
143
soc_dev = kzalloc(sizeof(*soc_dev), GFP_KERNEL);
144
if (!soc_dev) {
145
ret = -ENOMEM;
146
goto out1;
147
}
148
149
soc_attr_groups = kcalloc(3, sizeof(*soc_attr_groups), GFP_KERNEL);
150
if (!soc_attr_groups) {
151
ret = -ENOMEM;
152
goto out2;
153
}
154
soc_attr_groups[0] = &soc_attr_group;
155
soc_attr_groups[1] = soc_dev_attr->custom_attr_group;
156
157
/* Fetch a unique (reclaimable) SOC ID. */
158
ret = ida_alloc(&soc_ida, GFP_KERNEL);
159
if (ret < 0)
160
goto out3;
161
soc_dev->soc_dev_num = ret;
162
163
soc_dev->attr = soc_dev_attr;
164
soc_dev->dev.bus = &soc_bus_type;
165
soc_dev->dev.groups = soc_attr_groups;
166
soc_dev->dev.release = soc_release;
167
168
dev_set_name(&soc_dev->dev, "soc%d", soc_dev->soc_dev_num);
169
170
ret = device_register(&soc_dev->dev);
171
if (ret) {
172
put_device(&soc_dev->dev);
173
return ERR_PTR(ret);
174
}
175
176
return soc_dev;
177
178
out3:
179
kfree(soc_attr_groups);
180
out2:
181
kfree(soc_dev);
182
out1:
183
return ERR_PTR(ret);
184
}
185
EXPORT_SYMBOL_GPL(soc_device_register);
186
187
/* Ensure soc_dev->attr is freed after calling soc_device_unregister. */
188
void soc_device_unregister(struct soc_device *soc_dev)
189
{
190
device_unregister(&soc_dev->dev);
191
early_soc_dev_attr = NULL;
192
}
193
EXPORT_SYMBOL_GPL(soc_device_unregister);
194
195
static int __init soc_bus_register(void)
196
{
197
int ret;
198
199
ret = bus_register(&soc_bus_type);
200
if (ret)
201
return ret;
202
soc_bus_registered = true;
203
204
if (early_soc_dev_attr)
205
return PTR_ERR(soc_device_register(early_soc_dev_attr));
206
207
return 0;
208
}
209
core_initcall(soc_bus_register);
210
211
static int soc_device_match_attr(const struct soc_device_attribute *attr,
212
const struct soc_device_attribute *match)
213
{
214
if (match->machine &&
215
(!attr->machine || !glob_match(match->machine, attr->machine)))
216
return 0;
217
218
if (match->family &&
219
(!attr->family || !glob_match(match->family, attr->family)))
220
return 0;
221
222
if (match->revision &&
223
(!attr->revision || !glob_match(match->revision, attr->revision)))
224
return 0;
225
226
if (match->soc_id &&
227
(!attr->soc_id || !glob_match(match->soc_id, attr->soc_id)))
228
return 0;
229
230
return 1;
231
}
232
233
static int soc_device_match_one(struct device *dev, void *arg)
234
{
235
struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
236
237
return soc_device_match_attr(soc_dev->attr, arg);
238
}
239
240
/*
241
* soc_device_match - identify the SoC in the machine
242
* @matches: zero-terminated array of possible matches
243
*
244
* returns the first matching entry of the argument array, or NULL
245
* if none of them match.
246
*
247
* This function is meant as a helper in place of of_match_node()
248
* in cases where either no device tree is available or the information
249
* in a device node is insufficient to identify a particular variant
250
* by its compatible strings or other properties. For new devices,
251
* the DT binding should always provide unique compatible strings
252
* that allow the use of of_match_node() instead.
253
*
254
* The calling function can use the .data entry of the
255
* soc_device_attribute to pass a structure or function pointer for
256
* each entry.
257
*/
258
const struct soc_device_attribute *soc_device_match(
259
const struct soc_device_attribute *matches)
260
{
261
int ret;
262
263
if (!matches)
264
return NULL;
265
266
while (matches->machine || matches->family || matches->revision ||
267
matches->soc_id) {
268
ret = bus_for_each_dev(&soc_bus_type, NULL, (void *)matches,
269
soc_device_match_one);
270
if (ret < 0 && early_soc_dev_attr)
271
ret = soc_device_match_attr(early_soc_dev_attr,
272
matches);
273
if (ret < 0)
274
return NULL;
275
if (ret)
276
return matches;
277
278
matches++;
279
}
280
return NULL;
281
}
282
EXPORT_SYMBOL_GPL(soc_device_match);
283
284