Blame view

drivers/base/soc.c 6.57 KB
989d42e85   Greg Kroah-Hartman   driver core: add ...
1
  // SPDX-License-Identifier: GPL-2.0
74d1d82cd   Lee Jones   drivers/base: add...
2
3
4
5
  /*
   * Copyright (C) ST-Ericsson SA 2011
   *
   * Author: Lee Jones <lee.jones@linaro.org> for ST-Ericsson.
74d1d82cd   Lee Jones   drivers/base: add...
6
7
8
   */
  
  #include <linux/sysfs.h>
74d1d82cd   Lee Jones   drivers/base: add...
9
10
11
12
13
14
15
  #include <linux/init.h>
  #include <linux/stat.h>
  #include <linux/slab.h>
  #include <linux/idr.h>
  #include <linux/spinlock.h>
  #include <linux/sys_soc.h>
  #include <linux/err.h>
c97db7cc7   Arnd Bergmann   base: soc: Introd...
16
  #include <linux/glob.h>
74d1d82cd   Lee Jones   drivers/base: add...
17

3a4ffe930   Lee Jones   drivers/base: fix...
18
  static DEFINE_IDA(soc_ida);
74d1d82cd   Lee Jones   drivers/base: add...
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
  
  static ssize_t soc_info_get(struct device *dev,
  			    struct device_attribute *attr,
  			    char *buf);
  
  struct soc_device {
  	struct device dev;
  	struct soc_device_attribute *attr;
  	int soc_dev_num;
  };
  
  static struct bus_type soc_bus_type = {
  	.name  = "soc",
  };
  
  static DEVICE_ATTR(machine,  S_IRUGO, soc_info_get,  NULL);
  static DEVICE_ATTR(family,   S_IRUGO, soc_info_get,  NULL);
9aebf4de2   Bjorn Andersson   base: soc: Add se...
36
  static DEVICE_ATTR(serial_number, S_IRUGO, soc_info_get,  NULL);
74d1d82cd   Lee Jones   drivers/base: add...
37
38
39
40
41
42
43
  static DEVICE_ATTR(soc_id,   S_IRUGO, soc_info_get,  NULL);
  static DEVICE_ATTR(revision, S_IRUGO, soc_info_get,  NULL);
  
  struct device *soc_device_to_device(struct soc_device *soc_dev)
  {
  	return &soc_dev->dev;
  }
726592a9b   Al Viro   mode_t whack-a-mo...
44
  static umode_t soc_attribute_mode(struct kobject *kobj,
2071b9502   Lavinia Tache   drivers/base: use...
45
46
  				struct attribute *attr,
  				int index)
74d1d82cd   Lee Jones   drivers/base: add...
47
48
49
50
51
52
53
54
55
56
57
58
59
  {
  	struct device *dev = container_of(kobj, struct device, kobj);
  	struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
  
  	if ((attr == &dev_attr_machine.attr)
  	    && (soc_dev->attr->machine != NULL))
  		return attr->mode;
  	if ((attr == &dev_attr_family.attr)
  	    && (soc_dev->attr->family != NULL))
  		return attr->mode;
  	if ((attr == &dev_attr_revision.attr)
  	    && (soc_dev->attr->revision != NULL))
  		return attr->mode;
9aebf4de2   Bjorn Andersson   base: soc: Add se...
60
61
62
  	if ((attr == &dev_attr_serial_number.attr)
  	    && (soc_dev->attr->serial_number != NULL))
  		return attr->mode;
74d1d82cd   Lee Jones   drivers/base: add...
63
64
  	if ((attr == &dev_attr_soc_id.attr)
  	    && (soc_dev->attr->soc_id != NULL))
2071b9502   Lavinia Tache   drivers/base: use...
65
  		return attr->mode;
74d1d82cd   Lee Jones   drivers/base: add...
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
  
  	/* Unknown or unfilled attribute. */
  	return 0;
  }
  
  static ssize_t soc_info_get(struct device *dev,
  			    struct device_attribute *attr,
  			    char *buf)
  {
  	struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
  
  	if (attr == &dev_attr_machine)
  		return sprintf(buf, "%s
  ", soc_dev->attr->machine);
  	if (attr == &dev_attr_family)
  		return sprintf(buf, "%s
  ", soc_dev->attr->family);
  	if (attr == &dev_attr_revision)
  		return sprintf(buf, "%s
  ", soc_dev->attr->revision);
9aebf4de2   Bjorn Andersson   base: soc: Add se...
86
87
88
  	if (attr == &dev_attr_serial_number)
  		return sprintf(buf, "%s
  ", soc_dev->attr->serial_number);
74d1d82cd   Lee Jones   drivers/base: add...
89
90
91
92
93
94
95
96
97
98
99
  	if (attr == &dev_attr_soc_id)
  		return sprintf(buf, "%s
  ", soc_dev->attr->soc_id);
  
  	return -EINVAL;
  
  }
  
  static struct attribute *soc_attr[] = {
  	&dev_attr_machine.attr,
  	&dev_attr_family.attr,
9aebf4de2   Bjorn Andersson   base: soc: Add se...
100
  	&dev_attr_serial_number.attr,
74d1d82cd   Lee Jones   drivers/base: add...
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
  	&dev_attr_soc_id.attr,
  	&dev_attr_revision.attr,
  	NULL,
  };
  
  static const struct attribute_group soc_attr_group = {
  	.attrs = soc_attr,
  	.is_visible = soc_attribute_mode,
  };
  
  static const struct attribute_group *soc_attr_groups[] = {
  	&soc_attr_group,
  	NULL,
  };
  
  static void soc_release(struct device *dev)
  {
  	struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
  
  	kfree(soc_dev);
  }
6e12db376   Geert Uytterhoeven   base: soc: Allow ...
122
  static struct soc_device_attribute *early_soc_dev_attr;
74d1d82cd   Lee Jones   drivers/base: add...
123
124
125
126
  struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr)
  {
  	struct soc_device *soc_dev;
  	int ret;
1da1b3628   Geert Uytterhoeven   base: soc: Early ...
127
  	if (!soc_bus_type.p) {
6e12db376   Geert Uytterhoeven   base: soc: Allow ...
128
129
130
131
  		if (early_soc_dev_attr)
  			return ERR_PTR(-EBUSY);
  		early_soc_dev_attr = soc_dev_attr;
  		return NULL;
1da1b3628   Geert Uytterhoeven   base: soc: Early ...
132
  	}
74d1d82cd   Lee Jones   drivers/base: add...
133
134
  	soc_dev = kzalloc(sizeof(*soc_dev), GFP_KERNEL);
  	if (!soc_dev) {
2071b9502   Lavinia Tache   drivers/base: use...
135
  		ret = -ENOMEM;
74d1d82cd   Lee Jones   drivers/base: add...
136
137
138
139
  		goto out1;
  	}
  
  	/* Fetch a unique (reclaimable) SOC ID. */
cfcf6a91a   Lee Duncan   base: soc: siplif...
140
141
  	ret = ida_simple_get(&soc_ida, 0, 0, GFP_KERNEL);
  	if (ret < 0)
2071b9502   Lavinia Tache   drivers/base: use...
142
  		goto out2;
cfcf6a91a   Lee Duncan   base: soc: siplif...
143
  	soc_dev->soc_dev_num = ret;
74d1d82cd   Lee Jones   drivers/base: add...
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
  
  	soc_dev->attr = soc_dev_attr;
  	soc_dev->dev.bus = &soc_bus_type;
  	soc_dev->dev.groups = soc_attr_groups;
  	soc_dev->dev.release = soc_release;
  
  	dev_set_name(&soc_dev->dev, "soc%d", soc_dev->soc_dev_num);
  
  	ret = device_register(&soc_dev->dev);
  	if (ret)
  		goto out3;
  
  	return soc_dev;
  
  out3:
cfcf6a91a   Lee Duncan   base: soc: siplif...
159
  	ida_simple_remove(&soc_ida, soc_dev->soc_dev_num);
ef49ec1dc   Arvind Yadav   base: soc: use pu...
160
161
  	put_device(&soc_dev->dev);
  	soc_dev = NULL;
74d1d82cd   Lee Jones   drivers/base: add...
162
163
164
165
166
  out2:
  	kfree(soc_dev);
  out1:
  	return ERR_PTR(ret);
  }
f7ccc7a39   Vinod Koul   base: soc: Export...
167
  EXPORT_SYMBOL_GPL(soc_device_register);
74d1d82cd   Lee Jones   drivers/base: add...
168
169
170
171
  
  /* Ensure soc_dev->attr is freed prior to calling soc_device_unregister. */
  void soc_device_unregister(struct soc_device *soc_dev)
  {
cfcf6a91a   Lee Duncan   base: soc: siplif...
172
  	ida_simple_remove(&soc_ida, soc_dev->soc_dev_num);
74d1d82cd   Lee Jones   drivers/base: add...
173
174
  
  	device_unregister(&soc_dev->dev);
6e12db376   Geert Uytterhoeven   base: soc: Allow ...
175
  	early_soc_dev_attr = NULL;
74d1d82cd   Lee Jones   drivers/base: add...
176
  }
f7ccc7a39   Vinod Koul   base: soc: Export...
177
  EXPORT_SYMBOL_GPL(soc_device_unregister);
74d1d82cd   Lee Jones   drivers/base: add...
178
179
180
  
  static int __init soc_bus_register(void)
  {
6e12db376   Geert Uytterhoeven   base: soc: Allow ...
181
  	int ret;
1da1b3628   Geert Uytterhoeven   base: soc: Early ...
182

6e12db376   Geert Uytterhoeven   base: soc: Allow ...
183
184
185
186
187
188
189
190
  	ret = bus_register(&soc_bus_type);
  	if (ret)
  		return ret;
  
  	if (early_soc_dev_attr)
  		return PTR_ERR(soc_device_register(early_soc_dev_attr));
  
  	return 0;
74d1d82cd   Lee Jones   drivers/base: add...
191
192
  }
  core_initcall(soc_bus_register);
c97db7cc7   Arnd Bergmann   base: soc: Introd...
193

6e12db376   Geert Uytterhoeven   base: soc: Allow ...
194
195
  static int soc_device_match_attr(const struct soc_device_attribute *attr,
  				 const struct soc_device_attribute *match)
c97db7cc7   Arnd Bergmann   base: soc: Introd...
196
  {
c97db7cc7   Arnd Bergmann   base: soc: Introd...
197
  	if (match->machine &&
6e12db376   Geert Uytterhoeven   base: soc: Allow ...
198
  	    (!attr->machine || !glob_match(match->machine, attr->machine)))
c97db7cc7   Arnd Bergmann   base: soc: Introd...
199
200
201
  		return 0;
  
  	if (match->family &&
6e12db376   Geert Uytterhoeven   base: soc: Allow ...
202
  	    (!attr->family || !glob_match(match->family, attr->family)))
c97db7cc7   Arnd Bergmann   base: soc: Introd...
203
204
205
  		return 0;
  
  	if (match->revision &&
6e12db376   Geert Uytterhoeven   base: soc: Allow ...
206
  	    (!attr->revision || !glob_match(match->revision, attr->revision)))
c97db7cc7   Arnd Bergmann   base: soc: Introd...
207
208
209
  		return 0;
  
  	if (match->soc_id &&
6e12db376   Geert Uytterhoeven   base: soc: Allow ...
210
  	    (!attr->soc_id || !glob_match(match->soc_id, attr->soc_id)))
c97db7cc7   Arnd Bergmann   base: soc: Introd...
211
212
213
214
  		return 0;
  
  	return 1;
  }
6e12db376   Geert Uytterhoeven   base: soc: Allow ...
215
216
217
218
219
220
  static int soc_device_match_one(struct device *dev, void *arg)
  {
  	struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
  
  	return soc_device_match_attr(soc_dev->attr, arg);
  }
c97db7cc7   Arnd Bergmann   base: soc: Introd...
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
  /*
   * soc_device_match - identify the SoC in the machine
   * @matches: zero-terminated array of possible matches
   *
   * returns the first matching entry of the argument array, or NULL
   * if none of them match.
   *
   * This function is meant as a helper in place of of_match_node()
   * in cases where either no device tree is available or the information
   * in a device node is insufficient to identify a particular variant
   * by its compatible strings or other properties. For new devices,
   * the DT binding should always provide unique compatible strings
   * that allow the use of of_match_node() instead.
   *
   * The calling function can use the .data entry of the
   * soc_device_attribute to pass a structure or function pointer for
   * each entry.
   */
  const struct soc_device_attribute *soc_device_match(
  	const struct soc_device_attribute *matches)
  {
  	int ret = 0;
  
  	if (!matches)
  		return NULL;
  
  	while (!ret) {
  		if (!(matches->machine || matches->family ||
  		      matches->revision || matches->soc_id))
  			break;
  		ret = bus_for_each_dev(&soc_bus_type, NULL, (void *)matches,
  				       soc_device_match_one);
6e12db376   Geert Uytterhoeven   base: soc: Allow ...
253
254
255
  		if (ret < 0 && early_soc_dev_attr)
  			ret = soc_device_match_attr(early_soc_dev_attr,
  						    matches);
0656db9e4   Geert Uytterhoeven   base: soc: Let so...
256
257
  		if (ret < 0)
  			return NULL;
c97db7cc7   Arnd Bergmann   base: soc: Introd...
258
259
260
261
262
263
264
265
  		if (!ret)
  			matches++;
  		else
  			return matches;
  	}
  	return NULL;
  }
  EXPORT_SYMBOL_GPL(soc_device_match);