Blame view

drivers/base/soc.c 6.7 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

948b3edba   Joe Perches   drivers core: Mis...
20
21
22
  /* Prototype to allow declarations of DEVICE_ATTR(<foo>) before soc_info_show */
  static ssize_t soc_info_show(struct device *dev, struct device_attribute *attr,
  			     char *buf);
74d1d82cd   Lee Jones   drivers/base: add...
23
24
25
26
27
28
29
30
31
32
  
  struct soc_device {
  	struct device dev;
  	struct soc_device_attribute *attr;
  	int soc_dev_num;
  };
  
  static struct bus_type soc_bus_type = {
  	.name  = "soc",
  };
948b3edba   Joe Perches   drivers core: Mis...
33
34
35
36
37
  static DEVICE_ATTR(machine,		0444, soc_info_show,  NULL);
  static DEVICE_ATTR(family,		0444, soc_info_show,  NULL);
  static DEVICE_ATTR(serial_number,	0444, soc_info_show,  NULL);
  static DEVICE_ATTR(soc_id,		0444, soc_info_show,  NULL);
  static DEVICE_ATTR(revision,		0444, soc_info_show,  NULL);
74d1d82cd   Lee Jones   drivers/base: add...
38
39
40
41
42
  
  struct device *soc_device_to_device(struct soc_device *soc_dev)
  {
  	return &soc_dev->dev;
  }
726592a9b   Al Viro   mode_t whack-a-mo...
43
  static umode_t soc_attribute_mode(struct kobject *kobj,
2071b9502   Lavinia Tache   drivers/base: use...
44
45
  				struct attribute *attr,
  				int index)
74d1d82cd   Lee Jones   drivers/base: add...
46
  {
baf1d9c18   zhouchuangao   driver/base/soc: ...
47
  	struct device *dev = kobj_to_dev(kobj);
74d1d82cd   Lee Jones   drivers/base: add...
48
  	struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
948b3edba   Joe Perches   drivers core: Mis...
49
  	if ((attr == &dev_attr_machine.attr) && soc_dev->attr->machine)
74d1d82cd   Lee Jones   drivers/base: add...
50
  		return attr->mode;
948b3edba   Joe Perches   drivers core: Mis...
51
  	if ((attr == &dev_attr_family.attr) && soc_dev->attr->family)
74d1d82cd   Lee Jones   drivers/base: add...
52
  		return attr->mode;
948b3edba   Joe Perches   drivers core: Mis...
53
  	if ((attr == &dev_attr_revision.attr) && soc_dev->attr->revision)
74d1d82cd   Lee Jones   drivers/base: add...
54
  		return attr->mode;
948b3edba   Joe Perches   drivers core: Mis...
55
  	if ((attr == &dev_attr_serial_number.attr) && soc_dev->attr->serial_number)
9aebf4de2   Bjorn Andersson   base: soc: Add se...
56
  		return attr->mode;
948b3edba   Joe Perches   drivers core: Mis...
57
  	if ((attr == &dev_attr_soc_id.attr) && soc_dev->attr->soc_id)
2071b9502   Lavinia Tache   drivers/base: use...
58
  		return attr->mode;
74d1d82cd   Lee Jones   drivers/base: add...
59

948b3edba   Joe Perches   drivers core: Mis...
60
  	/* Unknown or unfilled attribute */
74d1d82cd   Lee Jones   drivers/base: add...
61
62
  	return 0;
  }
948b3edba   Joe Perches   drivers core: Mis...
63
64
  static ssize_t soc_info_show(struct device *dev, struct device_attribute *attr,
  			     char *buf)
74d1d82cd   Lee Jones   drivers/base: add...
65
66
  {
  	struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
948b3edba   Joe Perches   drivers core: Mis...
67
  	const char *output;
74d1d82cd   Lee Jones   drivers/base: add...
68
69
  
  	if (attr == &dev_attr_machine)
948b3edba   Joe Perches   drivers core: Mis...
70
71
72
73
74
75
76
77
78
79
80
81
82
83
  		output = soc_dev->attr->machine;
  	else if (attr == &dev_attr_family)
  		output = soc_dev->attr->family;
  	else if (attr == &dev_attr_revision)
  		output = soc_dev->attr->revision;
  	else if (attr == &dev_attr_serial_number)
  		output = soc_dev->attr->serial_number;
  	else if (attr == &dev_attr_soc_id)
  		output = soc_dev->attr->soc_id;
  	else
  		return -EINVAL;
  
  	return sysfs_emit(buf, "%s
  ", output);
74d1d82cd   Lee Jones   drivers/base: add...
84
85
86
87
88
  }
  
  static struct attribute *soc_attr[] = {
  	&dev_attr_machine.attr,
  	&dev_attr_family.attr,
9aebf4de2   Bjorn Andersson   base: soc: Add se...
89
  	&dev_attr_serial_number.attr,
74d1d82cd   Lee Jones   drivers/base: add...
90
91
92
93
94
95
96
97
98
  	&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,
  };
74d1d82cd   Lee Jones   drivers/base: add...
99
100
101
  static void soc_release(struct device *dev)
  {
  	struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
c31e73121   Murali Nalajala   base: soc: Handle...
102
103
  	ida_simple_remove(&soc_ida, soc_dev->soc_dev_num);
  	kfree(soc_dev->dev.groups);
74d1d82cd   Lee Jones   drivers/base: add...
104
105
  	kfree(soc_dev);
  }
6e12db376   Geert Uytterhoeven   base: soc: Allow ...
106
  static struct soc_device_attribute *early_soc_dev_attr;
74d1d82cd   Lee Jones   drivers/base: add...
107
108
109
  struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr)
  {
  	struct soc_device *soc_dev;
c31e73121   Murali Nalajala   base: soc: Handle...
110
  	const struct attribute_group **soc_attr_groups;
74d1d82cd   Lee Jones   drivers/base: add...
111
  	int ret;
1da1b3628   Geert Uytterhoeven   base: soc: Early ...
112
  	if (!soc_bus_type.p) {
6e12db376   Geert Uytterhoeven   base: soc: Allow ...
113
114
115
116
  		if (early_soc_dev_attr)
  			return ERR_PTR(-EBUSY);
  		early_soc_dev_attr = soc_dev_attr;
  		return NULL;
1da1b3628   Geert Uytterhoeven   base: soc: Early ...
117
  	}
74d1d82cd   Lee Jones   drivers/base: add...
118
119
  	soc_dev = kzalloc(sizeof(*soc_dev), GFP_KERNEL);
  	if (!soc_dev) {
2071b9502   Lavinia Tache   drivers/base: use...
120
  		ret = -ENOMEM;
74d1d82cd   Lee Jones   drivers/base: add...
121
122
  		goto out1;
  	}
c31e73121   Murali Nalajala   base: soc: Handle...
123
124
125
126
127
128
129
  	soc_attr_groups = kcalloc(3, sizeof(*soc_attr_groups), GFP_KERNEL);
  	if (!soc_attr_groups) {
  		ret = -ENOMEM;
  		goto out2;
  	}
  	soc_attr_groups[0] = &soc_attr_group;
  	soc_attr_groups[1] = soc_dev_attr->custom_attr_group;
74d1d82cd   Lee Jones   drivers/base: add...
130
  	/* Fetch a unique (reclaimable) SOC ID. */
cfcf6a91a   Lee Duncan   base: soc: siplif...
131
132
  	ret = ida_simple_get(&soc_ida, 0, 0, GFP_KERNEL);
  	if (ret < 0)
c31e73121   Murali Nalajala   base: soc: Handle...
133
  		goto out3;
cfcf6a91a   Lee Duncan   base: soc: siplif...
134
  	soc_dev->soc_dev_num = ret;
74d1d82cd   Lee Jones   drivers/base: add...
135
136
137
138
139
140
141
142
143
  
  	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);
c31e73121   Murali Nalajala   base: soc: Handle...
144
145
146
147
  	if (ret) {
  		put_device(&soc_dev->dev);
  		return ERR_PTR(ret);
  	}
74d1d82cd   Lee Jones   drivers/base: add...
148
149
150
151
  
  	return soc_dev;
  
  out3:
c31e73121   Murali Nalajala   base: soc: Handle...
152
  	kfree(soc_attr_groups);
74d1d82cd   Lee Jones   drivers/base: add...
153
154
155
156
157
  out2:
  	kfree(soc_dev);
  out1:
  	return ERR_PTR(ret);
  }
f7ccc7a39   Vinod Koul   base: soc: Export...
158
  EXPORT_SYMBOL_GPL(soc_device_register);
74d1d82cd   Lee Jones   drivers/base: add...
159
160
161
162
  
  /* Ensure soc_dev->attr is freed prior to calling soc_device_unregister. */
  void soc_device_unregister(struct soc_device *soc_dev)
  {
74d1d82cd   Lee Jones   drivers/base: add...
163
  	device_unregister(&soc_dev->dev);
6e12db376   Geert Uytterhoeven   base: soc: Allow ...
164
  	early_soc_dev_attr = NULL;
74d1d82cd   Lee Jones   drivers/base: add...
165
  }
f7ccc7a39   Vinod Koul   base: soc: Export...
166
  EXPORT_SYMBOL_GPL(soc_device_unregister);
74d1d82cd   Lee Jones   drivers/base: add...
167
168
169
  
  static int __init soc_bus_register(void)
  {
6e12db376   Geert Uytterhoeven   base: soc: Allow ...
170
  	int ret;
1da1b3628   Geert Uytterhoeven   base: soc: Early ...
171

6e12db376   Geert Uytterhoeven   base: soc: Allow ...
172
173
174
175
176
177
178
179
  	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...
180
181
  }
  core_initcall(soc_bus_register);
c97db7cc7   Arnd Bergmann   base: soc: Introd...
182

6e12db376   Geert Uytterhoeven   base: soc: Allow ...
183
184
  static int soc_device_match_attr(const struct soc_device_attribute *attr,
  				 const struct soc_device_attribute *match)
c97db7cc7   Arnd Bergmann   base: soc: Introd...
185
  {
c97db7cc7   Arnd Bergmann   base: soc: Introd...
186
  	if (match->machine &&
6e12db376   Geert Uytterhoeven   base: soc: Allow ...
187
  	    (!attr->machine || !glob_match(match->machine, attr->machine)))
c97db7cc7   Arnd Bergmann   base: soc: Introd...
188
189
190
  		return 0;
  
  	if (match->family &&
6e12db376   Geert Uytterhoeven   base: soc: Allow ...
191
  	    (!attr->family || !glob_match(match->family, attr->family)))
c97db7cc7   Arnd Bergmann   base: soc: Introd...
192
193
194
  		return 0;
  
  	if (match->revision &&
6e12db376   Geert Uytterhoeven   base: soc: Allow ...
195
  	    (!attr->revision || !glob_match(match->revision, attr->revision)))
c97db7cc7   Arnd Bergmann   base: soc: Introd...
196
197
198
  		return 0;
  
  	if (match->soc_id &&
6e12db376   Geert Uytterhoeven   base: soc: Allow ...
199
  	    (!attr->soc_id || !glob_match(match->soc_id, attr->soc_id)))
c97db7cc7   Arnd Bergmann   base: soc: Introd...
200
201
202
203
  		return 0;
  
  	return 1;
  }
6e12db376   Geert Uytterhoeven   base: soc: Allow ...
204
205
206
207
208
209
  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...
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
  /*
   * 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 ...
242
243
244
  		if (ret < 0 && early_soc_dev_attr)
  			ret = soc_device_match_attr(early_soc_dev_attr,
  						    matches);
0656db9e4   Geert Uytterhoeven   base: soc: Let so...
245
246
  		if (ret < 0)
  			return NULL;
c97db7cc7   Arnd Bergmann   base: soc: Introd...
247
248
249
250
251
252
253
254
  		if (!ret)
  			matches++;
  		else
  			return matches;
  	}
  	return NULL;
  }
  EXPORT_SYMBOL_GPL(soc_device_match);