xref: /openbmc/linux/drivers/base/cpu.c (revision 5aaba363)
11da177e4SLinus Torvalds /*
28a25a2fdSKay Sievers  * CPU subsystem support
31da177e4SLinus Torvalds  */
41da177e4SLinus Torvalds 
5024f7846SBen Hutchings #include <linux/kernel.h>
61da177e4SLinus Torvalds #include <linux/module.h>
71da177e4SLinus Torvalds #include <linux/init.h>
8f6a57033SAl Viro #include <linux/sched.h>
91da177e4SLinus Torvalds #include <linux/cpu.h>
101da177e4SLinus Torvalds #include <linux/topology.h>
111da177e4SLinus Torvalds #include <linux/device.h>
1276b67ed9SKAMEZAWA Hiroyuki #include <linux/node.h>
135a0e3ad6STejun Heo #include <linux/gfp.h>
14fad12ac8SThomas Renninger #include <linux/slab.h>
159f13a1fdSBen Hutchings #include <linux/percpu.h>
16ac212b69SRafael J. Wysocki #include <linux/acpi.h>
17f86e4718SSudeep KarkadaNagesha #include <linux/of.h>
1867bad2fdSArd Biesheuvel #include <linux/cpufeature.h>
191da177e4SLinus Torvalds 
20a1bdc7aaSBen Dooks #include "base.h"
211da177e4SLinus Torvalds 
228a25a2fdSKay Sievers static DEFINE_PER_CPU(struct device *, cpu_sys_devices);
23ad74557aSAshok Raj 
24ac212b69SRafael J. Wysocki static int cpu_subsys_match(struct device *dev, struct device_driver *drv)
25ac212b69SRafael J. Wysocki {
26ac212b69SRafael J. Wysocki 	/* ACPI style match is the only one that may succeed. */
27ac212b69SRafael J. Wysocki 	if (acpi_driver_match_device(dev, drv))
28ac212b69SRafael J. Wysocki 		return 1;
29ac212b69SRafael J. Wysocki 
30ac212b69SRafael J. Wysocki 	return 0;
31ac212b69SRafael J. Wysocki }
32ac212b69SRafael J. Wysocki 
331da177e4SLinus Torvalds #ifdef CONFIG_HOTPLUG_CPU
3434640468SYasuaki Ishimatsu static void change_cpu_under_node(struct cpu *cpu,
3534640468SYasuaki Ishimatsu 			unsigned int from_nid, unsigned int to_nid)
3634640468SYasuaki Ishimatsu {
3734640468SYasuaki Ishimatsu 	int cpuid = cpu->dev.id;
3834640468SYasuaki Ishimatsu 	unregister_cpu_under_node(cpuid, from_nid);
3934640468SYasuaki Ishimatsu 	register_cpu_under_node(cpuid, to_nid);
4034640468SYasuaki Ishimatsu 	cpu->node_id = to_nid;
4134640468SYasuaki Ishimatsu }
4234640468SYasuaki Ishimatsu 
430902a904SRafael J. Wysocki static int __ref cpu_subsys_online(struct device *dev)
441da177e4SLinus Torvalds {
458a25a2fdSKay Sievers 	struct cpu *cpu = container_of(dev, struct cpu, dev);
460902a904SRafael J. Wysocki 	int cpuid = dev->id;
4734640468SYasuaki Ishimatsu 	int from_nid, to_nid;
486dedcca6SToshi Kani 	int ret;
490902a904SRafael J. Wysocki 
5034640468SYasuaki Ishimatsu 	from_nid = cpu_to_node(cpuid);
51c7991b0bSRafael J. Wysocki 	if (from_nid == NUMA_NO_NODE)
526dedcca6SToshi Kani 		return -ENODEV;
53c7991b0bSRafael J. Wysocki 
5434640468SYasuaki Ishimatsu 	ret = cpu_up(cpuid);
5534640468SYasuaki Ishimatsu 	/*
5634640468SYasuaki Ishimatsu 	 * When hot adding memory to memoryless node and enabling a cpu
5734640468SYasuaki Ishimatsu 	 * on the node, node number of the cpu may internally change.
5834640468SYasuaki Ishimatsu 	 */
5934640468SYasuaki Ishimatsu 	to_nid = cpu_to_node(cpuid);
6034640468SYasuaki Ishimatsu 	if (from_nid != to_nid)
6134640468SYasuaki Ishimatsu 		change_cpu_under_node(cpu, from_nid, to_nid);
6234640468SYasuaki Ishimatsu 
631da177e4SLinus Torvalds 	return ret;
641da177e4SLinus Torvalds }
651da177e4SLinus Torvalds 
660902a904SRafael J. Wysocki static int cpu_subsys_offline(struct device *dev)
671da177e4SLinus Torvalds {
686dedcca6SToshi Kani 	return cpu_down(dev->id);
691da177e4SLinus Torvalds }
701c4e2d70SIgor Mammedov 
7176b67ed9SKAMEZAWA Hiroyuki void unregister_cpu(struct cpu *cpu)
721da177e4SLinus Torvalds {
738a25a2fdSKay Sievers 	int logical_cpu = cpu->dev.id;
741da177e4SLinus Torvalds 
7576b67ed9SKAMEZAWA Hiroyuki 	unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu));
7676b67ed9SKAMEZAWA Hiroyuki 
778a25a2fdSKay Sievers 	device_unregister(&cpu->dev);
78e37d05daSMike Travis 	per_cpu(cpu_sys_devices, logical_cpu) = NULL;
791da177e4SLinus Torvalds 	return;
801da177e4SLinus Torvalds }
8112633e80SNathan Fontenot 
8212633e80SNathan Fontenot #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
838a25a2fdSKay Sievers static ssize_t cpu_probe_store(struct device *dev,
848a25a2fdSKay Sievers 			       struct device_attribute *attr,
8528812fe1SAndi Kleen 			       const char *buf,
8612633e80SNathan Fontenot 			       size_t count)
8712633e80SNathan Fontenot {
88574b851eSToshi Kani 	ssize_t cnt;
89574b851eSToshi Kani 	int ret;
90574b851eSToshi Kani 
91574b851eSToshi Kani 	ret = lock_device_hotplug_sysfs();
92574b851eSToshi Kani 	if (ret)
93574b851eSToshi Kani 		return ret;
94574b851eSToshi Kani 
95574b851eSToshi Kani 	cnt = arch_cpu_probe(buf, count);
96574b851eSToshi Kani 
97574b851eSToshi Kani 	unlock_device_hotplug();
98574b851eSToshi Kani 	return cnt;
9912633e80SNathan Fontenot }
10012633e80SNathan Fontenot 
1018a25a2fdSKay Sievers static ssize_t cpu_release_store(struct device *dev,
1028a25a2fdSKay Sievers 				 struct device_attribute *attr,
10328812fe1SAndi Kleen 				 const char *buf,
10412633e80SNathan Fontenot 				 size_t count)
10512633e80SNathan Fontenot {
106574b851eSToshi Kani 	ssize_t cnt;
107574b851eSToshi Kani 	int ret;
108574b851eSToshi Kani 
109574b851eSToshi Kani 	ret = lock_device_hotplug_sysfs();
110574b851eSToshi Kani 	if (ret)
111574b851eSToshi Kani 		return ret;
112574b851eSToshi Kani 
113574b851eSToshi Kani 	cnt = arch_cpu_release(buf, count);
114574b851eSToshi Kani 
115574b851eSToshi Kani 	unlock_device_hotplug();
116574b851eSToshi Kani 	return cnt;
11712633e80SNathan Fontenot }
11812633e80SNathan Fontenot 
1198a25a2fdSKay Sievers static DEVICE_ATTR(probe, S_IWUSR, NULL, cpu_probe_store);
1208a25a2fdSKay Sievers static DEVICE_ATTR(release, S_IWUSR, NULL, cpu_release_store);
12112633e80SNathan Fontenot #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */
1221da177e4SLinus Torvalds #endif /* CONFIG_HOTPLUG_CPU */
1231da177e4SLinus Torvalds 
1240902a904SRafael J. Wysocki struct bus_type cpu_subsys = {
1250902a904SRafael J. Wysocki 	.name = "cpu",
1260902a904SRafael J. Wysocki 	.dev_name = "cpu",
127ac212b69SRafael J. Wysocki 	.match = cpu_subsys_match,
1280902a904SRafael J. Wysocki #ifdef CONFIG_HOTPLUG_CPU
1290902a904SRafael J. Wysocki 	.online = cpu_subsys_online,
1300902a904SRafael J. Wysocki 	.offline = cpu_subsys_offline,
1310902a904SRafael J. Wysocki #endif
1320902a904SRafael J. Wysocki };
1330902a904SRafael J. Wysocki EXPORT_SYMBOL_GPL(cpu_subsys);
1340902a904SRafael J. Wysocki 
13551be5606SVivek Goyal #ifdef CONFIG_KEXEC
13651be5606SVivek Goyal #include <linux/kexec.h>
13751be5606SVivek Goyal 
1388a25a2fdSKay Sievers static ssize_t show_crash_notes(struct device *dev, struct device_attribute *attr,
1394a0b2b4dSAndi Kleen 				char *buf)
14051be5606SVivek Goyal {
1418a25a2fdSKay Sievers 	struct cpu *cpu = container_of(dev, struct cpu, dev);
14251be5606SVivek Goyal 	ssize_t rc;
14351be5606SVivek Goyal 	unsigned long long addr;
14451be5606SVivek Goyal 	int cpunum;
14551be5606SVivek Goyal 
1468a25a2fdSKay Sievers 	cpunum = cpu->dev.id;
14751be5606SVivek Goyal 
14851be5606SVivek Goyal 	/*
14951be5606SVivek Goyal 	 * Might be reading other cpu's data based on which cpu read thread
15051be5606SVivek Goyal 	 * has been scheduled. But cpu data (memory) is allocated once during
15151be5606SVivek Goyal 	 * boot up and this data does not change there after. Hence this
15251be5606SVivek Goyal 	 * operation should be safe. No locking required.
15351be5606SVivek Goyal 	 */
1543b034b0dSVivek Goyal 	addr = per_cpu_ptr_to_phys(per_cpu_ptr(crash_notes, cpunum));
15551be5606SVivek Goyal 	rc = sprintf(buf, "%Lx\n", addr);
15651be5606SVivek Goyal 	return rc;
15751be5606SVivek Goyal }
1588a25a2fdSKay Sievers static DEVICE_ATTR(crash_notes, 0400, show_crash_notes, NULL);
159eca4549fSZhang Yanfei 
160eca4549fSZhang Yanfei static ssize_t show_crash_notes_size(struct device *dev,
161eca4549fSZhang Yanfei 				     struct device_attribute *attr,
162eca4549fSZhang Yanfei 				     char *buf)
163eca4549fSZhang Yanfei {
164eca4549fSZhang Yanfei 	ssize_t rc;
165eca4549fSZhang Yanfei 
166bcfb87fbSArnd Bergmann 	rc = sprintf(buf, "%zu\n", sizeof(note_buf_t));
167eca4549fSZhang Yanfei 	return rc;
168eca4549fSZhang Yanfei }
169eca4549fSZhang Yanfei static DEVICE_ATTR(crash_notes_size, 0400, show_crash_notes_size, NULL);
170c055da9fSIgor Mammedov 
171c055da9fSIgor Mammedov static struct attribute *crash_note_cpu_attrs[] = {
172c055da9fSIgor Mammedov 	&dev_attr_crash_notes.attr,
173c055da9fSIgor Mammedov 	&dev_attr_crash_notes_size.attr,
174c055da9fSIgor Mammedov 	NULL
175c055da9fSIgor Mammedov };
176c055da9fSIgor Mammedov 
177c055da9fSIgor Mammedov static struct attribute_group crash_note_cpu_attr_group = {
178c055da9fSIgor Mammedov 	.attrs = crash_note_cpu_attrs,
179c055da9fSIgor Mammedov };
18051be5606SVivek Goyal #endif
18151be5606SVivek Goyal 
182c055da9fSIgor Mammedov static const struct attribute_group *common_cpu_attr_groups[] = {
183c055da9fSIgor Mammedov #ifdef CONFIG_KEXEC
184c055da9fSIgor Mammedov 	&crash_note_cpu_attr_group,
185c055da9fSIgor Mammedov #endif
186c055da9fSIgor Mammedov 	NULL
187c055da9fSIgor Mammedov };
188c055da9fSIgor Mammedov 
1891c4e2d70SIgor Mammedov static const struct attribute_group *hotplugable_cpu_attr_groups[] = {
1901c4e2d70SIgor Mammedov #ifdef CONFIG_KEXEC
1911c4e2d70SIgor Mammedov 	&crash_note_cpu_attr_group,
1921c4e2d70SIgor Mammedov #endif
1931c4e2d70SIgor Mammedov 	NULL
1941c4e2d70SIgor Mammedov };
1951c4e2d70SIgor Mammedov 
1961da177e4SLinus Torvalds /*
1979d1fe323SMike Travis  * Print cpu online, possible, present, and system maps
1989d1fe323SMike Travis  */
199265d2e2eSAndi Kleen 
200265d2e2eSAndi Kleen struct cpu_attr {
2018a25a2fdSKay Sievers 	struct device_attribute attr;
202265d2e2eSAndi Kleen 	const struct cpumask *const * const map;
203265d2e2eSAndi Kleen };
204265d2e2eSAndi Kleen 
2058a25a2fdSKay Sievers static ssize_t show_cpus_attr(struct device *dev,
2068a25a2fdSKay Sievers 			      struct device_attribute *attr,
207265d2e2eSAndi Kleen 			      char *buf)
2089d1fe323SMike Travis {
209265d2e2eSAndi Kleen 	struct cpu_attr *ca = container_of(attr, struct cpu_attr, attr);
2109d1fe323SMike Travis 
2115aaba363SSudeep Holla 	return cpumap_print_to_pagebuf(true, buf, *ca->map);
2129d1fe323SMike Travis }
2139d1fe323SMike Travis 
214265d2e2eSAndi Kleen #define _CPU_ATTR(name, map) \
2158a25a2fdSKay Sievers 	{ __ATTR(name, 0444, show_cpus_attr, NULL), map }
2169d1fe323SMike Travis 
2178a25a2fdSKay Sievers /* Keep in sync with cpu_subsys_attrs */
218265d2e2eSAndi Kleen static struct cpu_attr cpu_attrs[] = {
219265d2e2eSAndi Kleen 	_CPU_ATTR(online, &cpu_online_mask),
220265d2e2eSAndi Kleen 	_CPU_ATTR(possible, &cpu_possible_mask),
221265d2e2eSAndi Kleen 	_CPU_ATTR(present, &cpu_present_mask),
222265d2e2eSAndi Kleen };
2239d1fe323SMike Travis 
224e057d7aeSMike Travis /*
225e057d7aeSMike Travis  * Print values for NR_CPUS and offlined cpus
226e057d7aeSMike Travis  */
2278a25a2fdSKay Sievers static ssize_t print_cpus_kernel_max(struct device *dev,
2288a25a2fdSKay Sievers 				     struct device_attribute *attr, char *buf)
229e057d7aeSMike Travis {
2308fd2d2d5SMike Travis 	int n = snprintf(buf, PAGE_SIZE-2, "%d\n", NR_CPUS - 1);
231e057d7aeSMike Travis 	return n;
232e057d7aeSMike Travis }
2338a25a2fdSKay Sievers static DEVICE_ATTR(kernel_max, 0444, print_cpus_kernel_max, NULL);
234e057d7aeSMike Travis 
235e057d7aeSMike Travis /* arch-optional setting to enable display of offline cpus >= nr_cpu_ids */
236e057d7aeSMike Travis unsigned int total_cpus;
237e057d7aeSMike Travis 
2388a25a2fdSKay Sievers static ssize_t print_cpus_offline(struct device *dev,
2398a25a2fdSKay Sievers 				  struct device_attribute *attr, char *buf)
240e057d7aeSMike Travis {
241e057d7aeSMike Travis 	int n = 0, len = PAGE_SIZE-2;
242e057d7aeSMike Travis 	cpumask_var_t offline;
243e057d7aeSMike Travis 
244e057d7aeSMike Travis 	/* display offline cpus < nr_cpu_ids */
245e057d7aeSMike Travis 	if (!alloc_cpumask_var(&offline, GFP_KERNEL))
246e057d7aeSMike Travis 		return -ENOMEM;
247cdc6e3d3SJan Beulich 	cpumask_andnot(offline, cpu_possible_mask, cpu_online_mask);
248e057d7aeSMike Travis 	n = cpulist_scnprintf(buf, len, offline);
249e057d7aeSMike Travis 	free_cpumask_var(offline);
250e057d7aeSMike Travis 
251e057d7aeSMike Travis 	/* display offline cpus >= nr_cpu_ids */
252e057d7aeSMike Travis 	if (total_cpus && nr_cpu_ids < total_cpus) {
253e057d7aeSMike Travis 		if (n && n < len)
254e057d7aeSMike Travis 			buf[n++] = ',';
255e057d7aeSMike Travis 
256e057d7aeSMike Travis 		if (nr_cpu_ids == total_cpus-1)
257e057d7aeSMike Travis 			n += snprintf(&buf[n], len - n, "%d", nr_cpu_ids);
258e057d7aeSMike Travis 		else
259e057d7aeSMike Travis 			n += snprintf(&buf[n], len - n, "%d-%d",
260e057d7aeSMike Travis 						      nr_cpu_ids, total_cpus-1);
261e057d7aeSMike Travis 	}
262e057d7aeSMike Travis 
263e057d7aeSMike Travis 	n += snprintf(&buf[n], len - n, "\n");
264e057d7aeSMike Travis 	return n;
265e057d7aeSMike Travis }
2668a25a2fdSKay Sievers static DEVICE_ATTR(offline, 0444, print_cpus_offline, NULL);
267e057d7aeSMike Travis 
2682885e25cSGreg Kroah-Hartman static void cpu_device_release(struct device *dev)
2692885e25cSGreg Kroah-Hartman {
2702885e25cSGreg Kroah-Hartman 	/*
2712885e25cSGreg Kroah-Hartman 	 * This is an empty function to prevent the driver core from spitting a
2722885e25cSGreg Kroah-Hartman 	 * warning at us.  Yes, I know this is directly opposite of what the
2732885e25cSGreg Kroah-Hartman 	 * documentation for the driver core and kobjects say, and the author
2742885e25cSGreg Kroah-Hartman 	 * of this code has already been publically ridiculed for doing
2752885e25cSGreg Kroah-Hartman 	 * something as foolish as this.  However, at this point in time, it is
2762885e25cSGreg Kroah-Hartman 	 * the only way to handle the issue of statically allocated cpu
2772885e25cSGreg Kroah-Hartman 	 * devices.  The different architectures will have their cpu device
2782885e25cSGreg Kroah-Hartman 	 * code reworked to properly handle this in the near future, so this
2792885e25cSGreg Kroah-Hartman 	 * function will then be changed to correctly free up the memory held
2802885e25cSGreg Kroah-Hartman 	 * by the cpu device.
2812885e25cSGreg Kroah-Hartman 	 *
2822885e25cSGreg Kroah-Hartman 	 * Never copy this way of doing things, or you too will be made fun of
28330a4840aSRalf Baechle 	 * on the linux-kernel list, you have been warned.
2842885e25cSGreg Kroah-Hartman 	 */
2852885e25cSGreg Kroah-Hartman }
2862885e25cSGreg Kroah-Hartman 
28767bad2fdSArd Biesheuvel #ifdef CONFIG_GENERIC_CPU_AUTOPROBE
28867bad2fdSArd Biesheuvel static ssize_t print_cpu_modalias(struct device *dev,
28967bad2fdSArd Biesheuvel 				  struct device_attribute *attr,
29067bad2fdSArd Biesheuvel 				  char *buf)
29167bad2fdSArd Biesheuvel {
29267bad2fdSArd Biesheuvel 	ssize_t n;
29367bad2fdSArd Biesheuvel 	u32 i;
29467bad2fdSArd Biesheuvel 
29567bad2fdSArd Biesheuvel 	n = sprintf(buf, "cpu:type:" CPU_FEATURE_TYPEFMT ":feature:",
29667bad2fdSArd Biesheuvel 		    CPU_FEATURE_TYPEVAL);
29767bad2fdSArd Biesheuvel 
29867bad2fdSArd Biesheuvel 	for (i = 0; i < MAX_CPU_FEATURES; i++)
29967bad2fdSArd Biesheuvel 		if (cpu_have_feature(i)) {
30067bad2fdSArd Biesheuvel 			if (PAGE_SIZE < n + sizeof(",XXXX\n")) {
30167bad2fdSArd Biesheuvel 				WARN(1, "CPU features overflow page\n");
30267bad2fdSArd Biesheuvel 				break;
30367bad2fdSArd Biesheuvel 			}
30467bad2fdSArd Biesheuvel 			n += sprintf(&buf[n], ",%04X", i);
30567bad2fdSArd Biesheuvel 		}
30667bad2fdSArd Biesheuvel 	buf[n++] = '\n';
30767bad2fdSArd Biesheuvel 	return n;
30867bad2fdSArd Biesheuvel }
30967bad2fdSArd Biesheuvel 
31067bad2fdSArd Biesheuvel static int cpu_uevent(struct device *dev, struct kobj_uevent_env *env)
31167bad2fdSArd Biesheuvel {
31267bad2fdSArd Biesheuvel 	char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
31367bad2fdSArd Biesheuvel 	if (buf) {
31467bad2fdSArd Biesheuvel 		print_cpu_modalias(NULL, NULL, buf);
31567bad2fdSArd Biesheuvel 		add_uevent_var(env, "MODALIAS=%s", buf);
31667bad2fdSArd Biesheuvel 		kfree(buf);
31767bad2fdSArd Biesheuvel 	}
31867bad2fdSArd Biesheuvel 	return 0;
31967bad2fdSArd Biesheuvel }
32067bad2fdSArd Biesheuvel #endif
32167bad2fdSArd Biesheuvel 
3229d1fe323SMike Travis /*
323405ae7d3SRobert P. J. Day  * register_cpu - Setup a sysfs device for a CPU.
32472486f1fSSiddha, Suresh B  * @cpu - cpu->hotpluggable field set to 1 will generate a control file in
32572486f1fSSiddha, Suresh B  *	  sysfs for this CPU.
3261da177e4SLinus Torvalds  * @num - CPU number to use when creating the device.
3271da177e4SLinus Torvalds  *
3281da177e4SLinus Torvalds  * Initialize and register the CPU device.
3291da177e4SLinus Torvalds  */
330a83048ebSPaul Gortmaker int register_cpu(struct cpu *cpu, int num)
3311da177e4SLinus Torvalds {
3321da177e4SLinus Torvalds 	int error;
3338a25a2fdSKay Sievers 
3341da177e4SLinus Torvalds 	cpu->node_id = cpu_to_node(num);
33529bb5d4fSGreg Kroah-Hartman 	memset(&cpu->dev, 0x00, sizeof(struct device));
3368a25a2fdSKay Sievers 	cpu->dev.id = num;
3378a25a2fdSKay Sievers 	cpu->dev.bus = &cpu_subsys;
3382885e25cSGreg Kroah-Hartman 	cpu->dev.release = cpu_device_release;
3390902a904SRafael J. Wysocki 	cpu->dev.offline_disabled = !cpu->hotpluggable;
3401001b4d4SToshi Kani 	cpu->dev.offline = !cpu_online(num);
341f86e4718SSudeep KarkadaNagesha 	cpu->dev.of_node = of_get_cpu_node(num, NULL);
3422b9c1f03SArd Biesheuvel #ifdef CONFIG_GENERIC_CPU_AUTOPROBE
34367bad2fdSArd Biesheuvel 	cpu->dev.bus->uevent = cpu_uevent;
344fad12ac8SThomas Renninger #endif
345c055da9fSIgor Mammedov 	cpu->dev.groups = common_cpu_attr_groups;
3461c4e2d70SIgor Mammedov 	if (cpu->hotpluggable)
3471c4e2d70SIgor Mammedov 		cpu->dev.groups = hotplugable_cpu_attr_groups;
3488a25a2fdSKay Sievers 	error = device_register(&cpu->dev);
349ad74557aSAshok Raj 	if (!error)
3508a25a2fdSKay Sievers 		per_cpu(cpu_sys_devices, num) = &cpu->dev;
35176b67ed9SKAMEZAWA Hiroyuki 	if (!error)
35276b67ed9SKAMEZAWA Hiroyuki 		register_cpu_under_node(num, cpu_to_node(num));
35351be5606SVivek Goyal 
3541da177e4SLinus Torvalds 	return error;
3551da177e4SLinus Torvalds }
3561da177e4SLinus Torvalds 
3578a25a2fdSKay Sievers struct device *get_cpu_device(unsigned cpu)
358ad74557aSAshok Raj {
359e37d05daSMike Travis 	if (cpu < nr_cpu_ids && cpu_possible(cpu))
360e37d05daSMike Travis 		return per_cpu(cpu_sys_devices, cpu);
361ad74557aSAshok Raj 	else
362ad74557aSAshok Raj 		return NULL;
363ad74557aSAshok Raj }
3648a25a2fdSKay Sievers EXPORT_SYMBOL_GPL(get_cpu_device);
3658a25a2fdSKay Sievers 
3662b9c1f03SArd Biesheuvel #ifdef CONFIG_GENERIC_CPU_AUTOPROBE
36767bad2fdSArd Biesheuvel static DEVICE_ATTR(modalias, 0444, print_cpu_modalias, NULL);
368fad12ac8SThomas Renninger #endif
369fad12ac8SThomas Renninger 
3708a25a2fdSKay Sievers static struct attribute *cpu_root_attrs[] = {
3718a25a2fdSKay Sievers #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
3728a25a2fdSKay Sievers 	&dev_attr_probe.attr,
3738a25a2fdSKay Sievers 	&dev_attr_release.attr,
3748a25a2fdSKay Sievers #endif
3758a25a2fdSKay Sievers 	&cpu_attrs[0].attr.attr,
3768a25a2fdSKay Sievers 	&cpu_attrs[1].attr.attr,
3778a25a2fdSKay Sievers 	&cpu_attrs[2].attr.attr,
3788a25a2fdSKay Sievers 	&dev_attr_kernel_max.attr,
3798a25a2fdSKay Sievers 	&dev_attr_offline.attr,
3802b9c1f03SArd Biesheuvel #ifdef CONFIG_GENERIC_CPU_AUTOPROBE
381fad12ac8SThomas Renninger 	&dev_attr_modalias.attr,
382fad12ac8SThomas Renninger #endif
3838a25a2fdSKay Sievers 	NULL
3848a25a2fdSKay Sievers };
3858a25a2fdSKay Sievers 
3868a25a2fdSKay Sievers static struct attribute_group cpu_root_attr_group = {
3878a25a2fdSKay Sievers 	.attrs = cpu_root_attrs,
3888a25a2fdSKay Sievers };
3898a25a2fdSKay Sievers 
3908a25a2fdSKay Sievers static const struct attribute_group *cpu_root_attr_groups[] = {
3918a25a2fdSKay Sievers 	&cpu_root_attr_group,
3928a25a2fdSKay Sievers 	NULL,
3938a25a2fdSKay Sievers };
3941da177e4SLinus Torvalds 
3952987557fSJosh Triplett bool cpu_is_hotpluggable(unsigned cpu)
3962987557fSJosh Triplett {
3977affca35SLinus Torvalds 	struct device *dev = get_cpu_device(cpu);
3987affca35SLinus Torvalds 	return dev && container_of(dev, struct cpu, dev)->hotpluggable;
3992987557fSJosh Triplett }
4002987557fSJosh Triplett EXPORT_SYMBOL_GPL(cpu_is_hotpluggable);
4012987557fSJosh Triplett 
4029f13a1fdSBen Hutchings #ifdef CONFIG_GENERIC_CPU_DEVICES
4039f13a1fdSBen Hutchings static DEFINE_PER_CPU(struct cpu, cpu_devices);
4049f13a1fdSBen Hutchings #endif
4059f13a1fdSBen Hutchings 
4069f13a1fdSBen Hutchings static void __init cpu_dev_register_generic(void)
4079f13a1fdSBen Hutchings {
4089f13a1fdSBen Hutchings #ifdef CONFIG_GENERIC_CPU_DEVICES
4099f13a1fdSBen Hutchings 	int i;
4109f13a1fdSBen Hutchings 
4119f13a1fdSBen Hutchings 	for_each_possible_cpu(i) {
4129f13a1fdSBen Hutchings 		if (register_cpu(&per_cpu(cpu_devices, i), i))
4139f13a1fdSBen Hutchings 			panic("Failed to register CPU device");
4149f13a1fdSBen Hutchings 	}
4159f13a1fdSBen Hutchings #endif
4169f13a1fdSBen Hutchings }
4179f13a1fdSBen Hutchings 
418024f7846SBen Hutchings void __init cpu_dev_init(void)
4191da177e4SLinus Torvalds {
420024f7846SBen Hutchings 	if (subsys_system_register(&cpu_subsys, cpu_root_attr_groups))
421024f7846SBen Hutchings 		panic("Failed to register CPU subsystem");
4225c45bf27SSiddha, Suresh B 
4239f13a1fdSBen Hutchings 	cpu_dev_register_generic();
4241da177e4SLinus Torvalds }
425