xref: /openbmc/linux/arch/powerpc/platforms/pseries/dlpar.c (revision 06bacefcbd5f91efb7ffedc17615fa188d6ce406)
1ab519a01SNathan Fontenot /*
2ab519a01SNathan Fontenot  * Support for dynamic reconfiguration for PCI, Memory, and CPU
3ab519a01SNathan Fontenot  * Hotplug and Dynamic Logical Partitioning on RPA platforms.
4ab519a01SNathan Fontenot  *
5ab519a01SNathan Fontenot  * Copyright (C) 2009 Nathan Fontenot
6ab519a01SNathan Fontenot  * Copyright (C) 2009 IBM Corporation
7ab519a01SNathan Fontenot  *
8ab519a01SNathan Fontenot  * This program is free software; you can redistribute it and/or
9ab519a01SNathan Fontenot  * modify it under the terms of the GNU General Public License version
10ab519a01SNathan Fontenot  * 2 as published by the Free Software Foundation.
11ab519a01SNathan Fontenot  */
12ab519a01SNathan Fontenot 
13999e2dadSNathan Fontenot #define pr_fmt(fmt)	"dlpar: " fmt
14999e2dadSNathan Fontenot 
15ab519a01SNathan Fontenot #include <linux/kernel.h>
16ab519a01SNathan Fontenot #include <linux/notifier.h>
17ab519a01SNathan Fontenot #include <linux/spinlock.h>
18ab519a01SNathan Fontenot #include <linux/cpu.h>
195a0e3ad6STejun Heo #include <linux/slab.h>
201cf3d8b3SNathan Fontenot #include <linux/of.h>
21*06bacefcSAndy Shevchenko 
22*06bacefcSAndy Shevchenko #include "of_helpers.h"
23b6db63d1SGautham R Shenoy #include "offline_states.h"
241217d34bSAnton Blanchard #include "pseries.h"
25ab519a01SNathan Fontenot 
26ab519a01SNathan Fontenot #include <asm/prom.h>
27ab519a01SNathan Fontenot #include <asm/machdep.h>
28ab519a01SNathan Fontenot #include <asm/uaccess.h>
29ab519a01SNathan Fontenot #include <asm/rtas.h>
30ab519a01SNathan Fontenot 
31ab519a01SNathan Fontenot struct cc_workarea {
32d6f1e7abSBharata B Rao 	__be32	drc_index;
33d6f1e7abSBharata B Rao 	__be32	zero;
34d6f1e7abSBharata B Rao 	__be32	name_offset;
35d6f1e7abSBharata B Rao 	__be32	prop_length;
36d6f1e7abSBharata B Rao 	__be32	prop_offset;
37ab519a01SNathan Fontenot };
38ab519a01SNathan Fontenot 
3920648974SNathan Fontenot void dlpar_free_cc_property(struct property *prop)
40ab519a01SNathan Fontenot {
41ab519a01SNathan Fontenot 	kfree(prop->name);
42ab519a01SNathan Fontenot 	kfree(prop->value);
43ab519a01SNathan Fontenot 	kfree(prop);
44ab519a01SNathan Fontenot }
45ab519a01SNathan Fontenot 
46ab519a01SNathan Fontenot static struct property *dlpar_parse_cc_property(struct cc_workarea *ccwa)
47ab519a01SNathan Fontenot {
48ab519a01SNathan Fontenot 	struct property *prop;
49ab519a01SNathan Fontenot 	char *name;
50ab519a01SNathan Fontenot 	char *value;
51ab519a01SNathan Fontenot 
52ab519a01SNathan Fontenot 	prop = kzalloc(sizeof(*prop), GFP_KERNEL);
53ab519a01SNathan Fontenot 	if (!prop)
54ab519a01SNathan Fontenot 		return NULL;
55ab519a01SNathan Fontenot 
56d6f1e7abSBharata B Rao 	name = (char *)ccwa + be32_to_cpu(ccwa->name_offset);
57ab519a01SNathan Fontenot 	prop->name = kstrdup(name, GFP_KERNEL);
58ab519a01SNathan Fontenot 
59d6f1e7abSBharata B Rao 	prop->length = be32_to_cpu(ccwa->prop_length);
60d6f1e7abSBharata B Rao 	value = (char *)ccwa + be32_to_cpu(ccwa->prop_offset);
61e72ed6b5SNishanth Aravamudan 	prop->value = kmemdup(value, prop->length, GFP_KERNEL);
62ab519a01SNathan Fontenot 	if (!prop->value) {
63ab519a01SNathan Fontenot 		dlpar_free_cc_property(prop);
64ab519a01SNathan Fontenot 		return NULL;
65ab519a01SNathan Fontenot 	}
66ab519a01SNathan Fontenot 
67ab519a01SNathan Fontenot 	return prop;
68ab519a01SNathan Fontenot }
69ab519a01SNathan Fontenot 
708d5ff320STyrel Datwyler static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa,
718d5ff320STyrel Datwyler 					       const char *path)
72ab519a01SNathan Fontenot {
73ab519a01SNathan Fontenot 	struct device_node *dn;
74ab519a01SNathan Fontenot 	char *name;
75ab519a01SNathan Fontenot 
768d5ff320STyrel Datwyler 	/* If parent node path is "/" advance path to NULL terminator to
778d5ff320STyrel Datwyler 	 * prevent double leading slashs in full_name.
788d5ff320STyrel Datwyler 	 */
798d5ff320STyrel Datwyler 	if (!path[1])
808d5ff320STyrel Datwyler 		path++;
818d5ff320STyrel Datwyler 
82ab519a01SNathan Fontenot 	dn = kzalloc(sizeof(*dn), GFP_KERNEL);
83ab519a01SNathan Fontenot 	if (!dn)
84ab519a01SNathan Fontenot 		return NULL;
85ab519a01SNathan Fontenot 
86d6f1e7abSBharata B Rao 	name = (char *)ccwa + be32_to_cpu(ccwa->name_offset);
878d5ff320STyrel Datwyler 	dn->full_name = kasprintf(GFP_KERNEL, "%s/%s", path, name);
88ab519a01SNathan Fontenot 	if (!dn->full_name) {
89ab519a01SNathan Fontenot 		kfree(dn);
90ab519a01SNathan Fontenot 		return NULL;
91ab519a01SNathan Fontenot 	}
92ab519a01SNathan Fontenot 
931578cb76STyrel Datwyler 	of_node_set_flag(dn, OF_DYNAMIC);
9497a9a717STyrel Datwyler 	of_node_init(dn);
951578cb76STyrel Datwyler 
96ab519a01SNathan Fontenot 	return dn;
97ab519a01SNathan Fontenot }
98ab519a01SNathan Fontenot 
99ab519a01SNathan Fontenot static void dlpar_free_one_cc_node(struct device_node *dn)
100ab519a01SNathan Fontenot {
101ab519a01SNathan Fontenot 	struct property *prop;
102ab519a01SNathan Fontenot 
103ab519a01SNathan Fontenot 	while (dn->properties) {
104ab519a01SNathan Fontenot 		prop = dn->properties;
105ab519a01SNathan Fontenot 		dn->properties = prop->next;
106ab519a01SNathan Fontenot 		dlpar_free_cc_property(prop);
107ab519a01SNathan Fontenot 	}
108ab519a01SNathan Fontenot 
109ab519a01SNathan Fontenot 	kfree(dn->full_name);
110ab519a01SNathan Fontenot 	kfree(dn);
111ab519a01SNathan Fontenot }
112ab519a01SNathan Fontenot 
11320648974SNathan Fontenot void dlpar_free_cc_nodes(struct device_node *dn)
114ab519a01SNathan Fontenot {
115ab519a01SNathan Fontenot 	if (dn->child)
116ab519a01SNathan Fontenot 		dlpar_free_cc_nodes(dn->child);
117ab519a01SNathan Fontenot 
118ab519a01SNathan Fontenot 	if (dn->sibling)
119ab519a01SNathan Fontenot 		dlpar_free_cc_nodes(dn->sibling);
120ab519a01SNathan Fontenot 
121ab519a01SNathan Fontenot 	dlpar_free_one_cc_node(dn);
122ab519a01SNathan Fontenot }
123ab519a01SNathan Fontenot 
1249c740025SAnton Blanchard #define COMPLETE	0
125ab519a01SNathan Fontenot #define NEXT_SIBLING    1
126ab519a01SNathan Fontenot #define NEXT_CHILD      2
127ab519a01SNathan Fontenot #define NEXT_PROPERTY   3
128ab519a01SNathan Fontenot #define PREV_PARENT     4
129ab519a01SNathan Fontenot #define MORE_MEMORY     5
130ab519a01SNathan Fontenot #define CALL_AGAIN	-2
131ab519a01SNathan Fontenot #define ERR_CFG_USE     -9003
132ab519a01SNathan Fontenot 
133d6f1e7abSBharata B Rao struct device_node *dlpar_configure_connector(__be32 drc_index,
1348d5ff320STyrel Datwyler 					      struct device_node *parent)
135ab519a01SNathan Fontenot {
136ab519a01SNathan Fontenot 	struct device_node *dn;
137ab519a01SNathan Fontenot 	struct device_node *first_dn = NULL;
138ab519a01SNathan Fontenot 	struct device_node *last_dn = NULL;
139ab519a01SNathan Fontenot 	struct property *property;
140ab519a01SNathan Fontenot 	struct property *last_property = NULL;
141ab519a01SNathan Fontenot 	struct cc_workarea *ccwa;
14293f68f1eSNathan Fontenot 	char *data_buf;
1438d5ff320STyrel Datwyler 	const char *parent_path = parent->full_name;
144ab519a01SNathan Fontenot 	int cc_token;
14593f68f1eSNathan Fontenot 	int rc = -1;
146ab519a01SNathan Fontenot 
147ab519a01SNathan Fontenot 	cc_token = rtas_token("ibm,configure-connector");
148ab519a01SNathan Fontenot 	if (cc_token == RTAS_UNKNOWN_SERVICE)
149ab519a01SNathan Fontenot 		return NULL;
150ab519a01SNathan Fontenot 
15193f68f1eSNathan Fontenot 	data_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
15293f68f1eSNathan Fontenot 	if (!data_buf)
15393f68f1eSNathan Fontenot 		return NULL;
15493f68f1eSNathan Fontenot 
15593f68f1eSNathan Fontenot 	ccwa = (struct cc_workarea *)&data_buf[0];
156ab519a01SNathan Fontenot 	ccwa->drc_index = drc_index;
157ab519a01SNathan Fontenot 	ccwa->zero = 0;
158ab519a01SNathan Fontenot 
15993f68f1eSNathan Fontenot 	do {
16093f68f1eSNathan Fontenot 		/* Since we release the rtas_data_buf lock between configure
16193f68f1eSNathan Fontenot 		 * connector calls we want to re-populate the rtas_data_buffer
16293f68f1eSNathan Fontenot 		 * with the contents of the previous call.
16393f68f1eSNathan Fontenot 		 */
16493f68f1eSNathan Fontenot 		spin_lock(&rtas_data_buf_lock);
16593f68f1eSNathan Fontenot 
16693f68f1eSNathan Fontenot 		memcpy(rtas_data_buf, data_buf, RTAS_DATA_BUF_SIZE);
167ab519a01SNathan Fontenot 		rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL);
16893f68f1eSNathan Fontenot 		memcpy(data_buf, rtas_data_buf, RTAS_DATA_BUF_SIZE);
16993f68f1eSNathan Fontenot 
17093f68f1eSNathan Fontenot 		spin_unlock(&rtas_data_buf_lock);
17193f68f1eSNathan Fontenot 
172ab519a01SNathan Fontenot 		switch (rc) {
1739c740025SAnton Blanchard 		case COMPLETE:
1749c740025SAnton Blanchard 			break;
1759c740025SAnton Blanchard 
176ab519a01SNathan Fontenot 		case NEXT_SIBLING:
1778d5ff320STyrel Datwyler 			dn = dlpar_parse_cc_node(ccwa, parent_path);
178ab519a01SNathan Fontenot 			if (!dn)
179ab519a01SNathan Fontenot 				goto cc_error;
180ab519a01SNathan Fontenot 
181ab519a01SNathan Fontenot 			dn->parent = last_dn->parent;
182ab519a01SNathan Fontenot 			last_dn->sibling = dn;
183ab519a01SNathan Fontenot 			last_dn = dn;
184ab519a01SNathan Fontenot 			break;
185ab519a01SNathan Fontenot 
186ab519a01SNathan Fontenot 		case NEXT_CHILD:
1878d5ff320STyrel Datwyler 			if (first_dn)
1888d5ff320STyrel Datwyler 				parent_path = last_dn->full_name;
1898d5ff320STyrel Datwyler 
1908d5ff320STyrel Datwyler 			dn = dlpar_parse_cc_node(ccwa, parent_path);
191ab519a01SNathan Fontenot 			if (!dn)
192ab519a01SNathan Fontenot 				goto cc_error;
193ab519a01SNathan Fontenot 
1948d5ff320STyrel Datwyler 			if (!first_dn) {
1958d5ff320STyrel Datwyler 				dn->parent = parent;
196ab519a01SNathan Fontenot 				first_dn = dn;
1978d5ff320STyrel Datwyler 			} else {
198ab519a01SNathan Fontenot 				dn->parent = last_dn;
199ab519a01SNathan Fontenot 				if (last_dn)
200ab519a01SNathan Fontenot 					last_dn->child = dn;
201ab519a01SNathan Fontenot 			}
202ab519a01SNathan Fontenot 
203ab519a01SNathan Fontenot 			last_dn = dn;
204ab519a01SNathan Fontenot 			break;
205ab519a01SNathan Fontenot 
206ab519a01SNathan Fontenot 		case NEXT_PROPERTY:
207ab519a01SNathan Fontenot 			property = dlpar_parse_cc_property(ccwa);
208ab519a01SNathan Fontenot 			if (!property)
209ab519a01SNathan Fontenot 				goto cc_error;
210ab519a01SNathan Fontenot 
211ab519a01SNathan Fontenot 			if (!last_dn->properties)
212ab519a01SNathan Fontenot 				last_dn->properties = property;
213ab519a01SNathan Fontenot 			else
214ab519a01SNathan Fontenot 				last_property->next = property;
215ab519a01SNathan Fontenot 
216ab519a01SNathan Fontenot 			last_property = property;
217ab519a01SNathan Fontenot 			break;
218ab519a01SNathan Fontenot 
219ab519a01SNathan Fontenot 		case PREV_PARENT:
220ab519a01SNathan Fontenot 			last_dn = last_dn->parent;
2218d5ff320STyrel Datwyler 			parent_path = last_dn->parent->full_name;
222ab519a01SNathan Fontenot 			break;
223ab519a01SNathan Fontenot 
224ab519a01SNathan Fontenot 		case CALL_AGAIN:
225ab519a01SNathan Fontenot 			break;
226ab519a01SNathan Fontenot 
227ab519a01SNathan Fontenot 		case MORE_MEMORY:
228ab519a01SNathan Fontenot 		case ERR_CFG_USE:
229ab519a01SNathan Fontenot 		default:
230ab519a01SNathan Fontenot 			printk(KERN_ERR "Unexpected Error (%d) "
231ab519a01SNathan Fontenot 			       "returned from configure-connector\n", rc);
232ab519a01SNathan Fontenot 			goto cc_error;
233ab519a01SNathan Fontenot 		}
23493f68f1eSNathan Fontenot 	} while (rc);
235ab519a01SNathan Fontenot 
236ab519a01SNathan Fontenot cc_error:
23793f68f1eSNathan Fontenot 	kfree(data_buf);
23893f68f1eSNathan Fontenot 
23993f68f1eSNathan Fontenot 	if (rc) {
240ab519a01SNathan Fontenot 		if (first_dn)
241ab519a01SNathan Fontenot 			dlpar_free_cc_nodes(first_dn);
24293f68f1eSNathan Fontenot 
243ab519a01SNathan Fontenot 		return NULL;
244ab519a01SNathan Fontenot 	}
245ab519a01SNathan Fontenot 
24693f68f1eSNathan Fontenot 	return first_dn;
24793f68f1eSNathan Fontenot }
24893f68f1eSNathan Fontenot 
249ab519a01SNathan Fontenot int dlpar_attach_node(struct device_node *dn)
250ab519a01SNathan Fontenot {
251ab519a01SNathan Fontenot 	int rc;
252ab519a01SNathan Fontenot 
253*06bacefcSAndy Shevchenko 	dn->parent = pseries_of_derive_parent(dn->full_name);
254*06bacefcSAndy Shevchenko 	if (IS_ERR(dn->parent))
255*06bacefcSAndy Shevchenko 		return PTR_ERR(dn->parent);
256ab519a01SNathan Fontenot 
2571cf3d8b3SNathan Fontenot 	rc = of_attach_node(dn);
2583aef19f0SAkinobu Mita 	if (rc) {
259ab519a01SNathan Fontenot 		printk(KERN_ERR "Failed to add device node %s\n",
260ab519a01SNathan Fontenot 		       dn->full_name);
2613aef19f0SAkinobu Mita 		return rc;
262ab519a01SNathan Fontenot 	}
263ab519a01SNathan Fontenot 
264ab519a01SNathan Fontenot 	of_node_put(dn->parent);
265ab519a01SNathan Fontenot 	return 0;
266ab519a01SNathan Fontenot }
267ab519a01SNathan Fontenot 
268ab519a01SNathan Fontenot int dlpar_detach_node(struct device_node *dn)
269ab519a01SNathan Fontenot {
2705935ff43STyrel Datwyler 	struct device_node *child;
2711cf3d8b3SNathan Fontenot 	int rc;
272ab519a01SNathan Fontenot 
2735935ff43STyrel Datwyler 	child = of_get_next_child(dn, NULL);
2745935ff43STyrel Datwyler 	while (child) {
2755935ff43STyrel Datwyler 		dlpar_detach_node(child);
2765935ff43STyrel Datwyler 		child = of_get_next_child(dn, child);
2775935ff43STyrel Datwyler 	}
2785935ff43STyrel Datwyler 
2791cf3d8b3SNathan Fontenot 	rc = of_detach_node(dn);
2801cf3d8b3SNathan Fontenot 	if (rc)
2811cf3d8b3SNathan Fontenot 		return rc;
2821cf3d8b3SNathan Fontenot 
2831cf3d8b3SNathan Fontenot 	of_node_put(dn); /* Must decrement the refcount */
284ab519a01SNathan Fontenot 	return 0;
285ab519a01SNathan Fontenot }
286ab519a01SNathan Fontenot 
287ab519a01SNathan Fontenot #define DR_ENTITY_SENSE		9003
288ab519a01SNathan Fontenot #define DR_ENTITY_PRESENT	1
289ab519a01SNathan Fontenot #define DR_ENTITY_UNUSABLE	2
290ab519a01SNathan Fontenot #define ALLOCATION_STATE	9003
291ab519a01SNathan Fontenot #define ALLOC_UNUSABLE		0
292ab519a01SNathan Fontenot #define ALLOC_USABLE		1
293ab519a01SNathan Fontenot #define ISOLATION_STATE		9001
294ab519a01SNathan Fontenot #define ISOLATE			0
295ab519a01SNathan Fontenot #define UNISOLATE		1
296ab519a01SNathan Fontenot 
297ab519a01SNathan Fontenot int dlpar_acquire_drc(u32 drc_index)
298ab519a01SNathan Fontenot {
299ab519a01SNathan Fontenot 	int dr_status, rc;
300ab519a01SNathan Fontenot 
301ab519a01SNathan Fontenot 	rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
302ab519a01SNathan Fontenot 		       DR_ENTITY_SENSE, drc_index);
303ab519a01SNathan Fontenot 	if (rc || dr_status != DR_ENTITY_UNUSABLE)
304ab519a01SNathan Fontenot 		return -1;
305ab519a01SNathan Fontenot 
306ab519a01SNathan Fontenot 	rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE);
307ab519a01SNathan Fontenot 	if (rc)
308ab519a01SNathan Fontenot 		return rc;
309ab519a01SNathan Fontenot 
310ab519a01SNathan Fontenot 	rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
311ab519a01SNathan Fontenot 	if (rc) {
312ab519a01SNathan Fontenot 		rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
313ab519a01SNathan Fontenot 		return rc;
314ab519a01SNathan Fontenot 	}
315ab519a01SNathan Fontenot 
316ab519a01SNathan Fontenot 	return 0;
317ab519a01SNathan Fontenot }
318ab519a01SNathan Fontenot 
319ab519a01SNathan Fontenot int dlpar_release_drc(u32 drc_index)
320ab519a01SNathan Fontenot {
321ab519a01SNathan Fontenot 	int dr_status, rc;
322ab519a01SNathan Fontenot 
323ab519a01SNathan Fontenot 	rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
324ab519a01SNathan Fontenot 		       DR_ENTITY_SENSE, drc_index);
325ab519a01SNathan Fontenot 	if (rc || dr_status != DR_ENTITY_PRESENT)
326ab519a01SNathan Fontenot 		return -1;
327ab519a01SNathan Fontenot 
328ab519a01SNathan Fontenot 	rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE);
329ab519a01SNathan Fontenot 	if (rc)
330ab519a01SNathan Fontenot 		return rc;
331ab519a01SNathan Fontenot 
332ab519a01SNathan Fontenot 	rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
333ab519a01SNathan Fontenot 	if (rc) {
334ab519a01SNathan Fontenot 		rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
335ab519a01SNathan Fontenot 		return rc;
336ab519a01SNathan Fontenot 	}
337ab519a01SNathan Fontenot 
338ab519a01SNathan Fontenot 	return 0;
339ab519a01SNathan Fontenot }
340ab519a01SNathan Fontenot 
3411a8061c4SNathan Fontenot #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
342ab519a01SNathan Fontenot 
343275a64f6SNathan Fontenot static int dlpar_online_cpu(struct device_node *dn)
344275a64f6SNathan Fontenot {
345275a64f6SNathan Fontenot 	int rc = 0;
346275a64f6SNathan Fontenot 	unsigned int cpu;
347275a64f6SNathan Fontenot 	int len, nthreads, i;
348822e7122SThomas Falcon 	const __be32 *intserv;
349822e7122SThomas Falcon 	u32 thread;
350275a64f6SNathan Fontenot 
351275a64f6SNathan Fontenot 	intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len);
352275a64f6SNathan Fontenot 	if (!intserv)
353275a64f6SNathan Fontenot 		return -EINVAL;
354275a64f6SNathan Fontenot 
355275a64f6SNathan Fontenot 	nthreads = len / sizeof(u32);
356275a64f6SNathan Fontenot 
357275a64f6SNathan Fontenot 	cpu_maps_update_begin();
358275a64f6SNathan Fontenot 	for (i = 0; i < nthreads; i++) {
359822e7122SThomas Falcon 		thread = be32_to_cpu(intserv[i]);
360275a64f6SNathan Fontenot 		for_each_present_cpu(cpu) {
361822e7122SThomas Falcon 			if (get_hard_smp_processor_id(cpu) != thread)
362275a64f6SNathan Fontenot 				continue;
363275a64f6SNathan Fontenot 			BUG_ON(get_cpu_current_state(cpu)
364275a64f6SNathan Fontenot 					!= CPU_STATE_OFFLINE);
365275a64f6SNathan Fontenot 			cpu_maps_update_done();
36610ccaf17SDan Streetman 			rc = device_online(get_cpu_device(cpu));
367275a64f6SNathan Fontenot 			if (rc)
368275a64f6SNathan Fontenot 				goto out;
369275a64f6SNathan Fontenot 			cpu_maps_update_begin();
370275a64f6SNathan Fontenot 
371275a64f6SNathan Fontenot 			break;
372275a64f6SNathan Fontenot 		}
373275a64f6SNathan Fontenot 		if (cpu == num_possible_cpus())
374275a64f6SNathan Fontenot 			printk(KERN_WARNING "Could not find cpu to online "
375822e7122SThomas Falcon 			       "with physical id 0x%x\n", thread);
376275a64f6SNathan Fontenot 	}
377275a64f6SNathan Fontenot 	cpu_maps_update_done();
378275a64f6SNathan Fontenot 
379275a64f6SNathan Fontenot out:
380275a64f6SNathan Fontenot 	return rc;
381275a64f6SNathan Fontenot 
382275a64f6SNathan Fontenot }
383275a64f6SNathan Fontenot 
3841a8061c4SNathan Fontenot static ssize_t dlpar_cpu_probe(const char *buf, size_t count)
3851a8061c4SNathan Fontenot {
3868d5ff320STyrel Datwyler 	struct device_node *dn, *parent;
3871618bd53SDaniel Walter 	u32 drc_index;
3881a8061c4SNathan Fontenot 	int rc;
3891a8061c4SNathan Fontenot 
3901618bd53SDaniel Walter 	rc = kstrtou32(buf, 0, &drc_index);
3916dedcca6SToshi Kani 	if (rc)
3926dedcca6SToshi Kani 		return -EINVAL;
3931a8061c4SNathan Fontenot 
394f32393c9SNathan Fontenot 	rc = dlpar_acquire_drc(drc_index);
395f32393c9SNathan Fontenot 	if (rc)
396f32393c9SNathan Fontenot 		return -EINVAL;
397f32393c9SNathan Fontenot 
3988d5ff320STyrel Datwyler 	parent = of_find_node_by_path("/cpus");
3996dedcca6SToshi Kani 	if (!parent)
4006dedcca6SToshi Kani 		return -ENODEV;
4018d5ff320STyrel Datwyler 
402d6f1e7abSBharata B Rao 	dn = dlpar_configure_connector(cpu_to_be32(drc_index), parent);
4032222ce0fSNathan Fontenot 	of_node_put(parent);
404daebaabbSBharata B Rao 	if (!dn) {
405daebaabbSBharata B Rao 		dlpar_release_drc(drc_index);
4066dedcca6SToshi Kani 		return -EINVAL;
407daebaabbSBharata B Rao 	}
4081a8061c4SNathan Fontenot 
4091a8061c4SNathan Fontenot 	rc = dlpar_attach_node(dn);
4101a8061c4SNathan Fontenot 	if (rc) {
4111a8061c4SNathan Fontenot 		dlpar_release_drc(drc_index);
4121a8061c4SNathan Fontenot 		dlpar_free_cc_nodes(dn);
4136dedcca6SToshi Kani 		return rc;
4141a8061c4SNathan Fontenot 	}
4151a8061c4SNathan Fontenot 
416275a64f6SNathan Fontenot 	rc = dlpar_online_cpu(dn);
4176dedcca6SToshi Kani 	if (rc)
4186dedcca6SToshi Kani 		return rc;
419b6db63d1SGautham R Shenoy 
4206dedcca6SToshi Kani 	return count;
4211a8061c4SNathan Fontenot }
4221a8061c4SNathan Fontenot 
423275a64f6SNathan Fontenot static int dlpar_offline_cpu(struct device_node *dn)
424275a64f6SNathan Fontenot {
425275a64f6SNathan Fontenot 	int rc = 0;
426275a64f6SNathan Fontenot 	unsigned int cpu;
427275a64f6SNathan Fontenot 	int len, nthreads, i;
428e36d1227SThomas Falcon 	const __be32 *intserv;
429e36d1227SThomas Falcon 	u32 thread;
430275a64f6SNathan Fontenot 
431275a64f6SNathan Fontenot 	intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len);
432275a64f6SNathan Fontenot 	if (!intserv)
433275a64f6SNathan Fontenot 		return -EINVAL;
434275a64f6SNathan Fontenot 
435275a64f6SNathan Fontenot 	nthreads = len / sizeof(u32);
436275a64f6SNathan Fontenot 
437275a64f6SNathan Fontenot 	cpu_maps_update_begin();
438275a64f6SNathan Fontenot 	for (i = 0; i < nthreads; i++) {
439e36d1227SThomas Falcon 		thread = be32_to_cpu(intserv[i]);
440275a64f6SNathan Fontenot 		for_each_present_cpu(cpu) {
441e36d1227SThomas Falcon 			if (get_hard_smp_processor_id(cpu) != thread)
442275a64f6SNathan Fontenot 				continue;
443275a64f6SNathan Fontenot 
444275a64f6SNathan Fontenot 			if (get_cpu_current_state(cpu) == CPU_STATE_OFFLINE)
445275a64f6SNathan Fontenot 				break;
446275a64f6SNathan Fontenot 
447275a64f6SNathan Fontenot 			if (get_cpu_current_state(cpu) == CPU_STATE_ONLINE) {
448ceddee23SRobert Jennings 				set_preferred_offline_state(cpu, CPU_STATE_OFFLINE);
449275a64f6SNathan Fontenot 				cpu_maps_update_done();
45010ccaf17SDan Streetman 				rc = device_offline(get_cpu_device(cpu));
451275a64f6SNathan Fontenot 				if (rc)
452275a64f6SNathan Fontenot 					goto out;
453275a64f6SNathan Fontenot 				cpu_maps_update_begin();
454275a64f6SNathan Fontenot 				break;
455275a64f6SNathan Fontenot 
456275a64f6SNathan Fontenot 			}
457275a64f6SNathan Fontenot 
458275a64f6SNathan Fontenot 			/*
459275a64f6SNathan Fontenot 			 * The cpu is in CPU_STATE_INACTIVE.
460275a64f6SNathan Fontenot 			 * Upgrade it's state to CPU_STATE_OFFLINE.
461275a64f6SNathan Fontenot 			 */
462275a64f6SNathan Fontenot 			set_preferred_offline_state(cpu, CPU_STATE_OFFLINE);
463e36d1227SThomas Falcon 			BUG_ON(plpar_hcall_norets(H_PROD, thread)
464275a64f6SNathan Fontenot 								!= H_SUCCESS);
465275a64f6SNathan Fontenot 			__cpu_die(cpu);
466275a64f6SNathan Fontenot 			break;
467275a64f6SNathan Fontenot 		}
468275a64f6SNathan Fontenot 		if (cpu == num_possible_cpus())
469275a64f6SNathan Fontenot 			printk(KERN_WARNING "Could not find cpu to offline "
470e36d1227SThomas Falcon 			       "with physical id 0x%x\n", thread);
471275a64f6SNathan Fontenot 	}
472275a64f6SNathan Fontenot 	cpu_maps_update_done();
473275a64f6SNathan Fontenot 
474275a64f6SNathan Fontenot out:
475275a64f6SNathan Fontenot 	return rc;
476275a64f6SNathan Fontenot 
477275a64f6SNathan Fontenot }
478275a64f6SNathan Fontenot 
4791a8061c4SNathan Fontenot static ssize_t dlpar_cpu_release(const char *buf, size_t count)
4801a8061c4SNathan Fontenot {
4811a8061c4SNathan Fontenot 	struct device_node *dn;
482e36d1227SThomas Falcon 	u32 drc_index;
4831a8061c4SNathan Fontenot 	int rc;
4841a8061c4SNathan Fontenot 
4851a8061c4SNathan Fontenot 	dn = of_find_node_by_path(buf);
4861a8061c4SNathan Fontenot 	if (!dn)
4871a8061c4SNathan Fontenot 		return -EINVAL;
4881a8061c4SNathan Fontenot 
489e36d1227SThomas Falcon 	rc = of_property_read_u32(dn, "ibm,my-drc-index", &drc_index);
490e36d1227SThomas Falcon 	if (rc) {
4911a8061c4SNathan Fontenot 		of_node_put(dn);
4921a8061c4SNathan Fontenot 		return -EINVAL;
4931a8061c4SNathan Fontenot 	}
4941a8061c4SNathan Fontenot 
495275a64f6SNathan Fontenot 	rc = dlpar_offline_cpu(dn);
496b6db63d1SGautham R Shenoy 	if (rc) {
497b6db63d1SGautham R Shenoy 		of_node_put(dn);
4986dedcca6SToshi Kani 		return -EINVAL;
499b6db63d1SGautham R Shenoy 	}
500b6db63d1SGautham R Shenoy 
501e36d1227SThomas Falcon 	rc = dlpar_release_drc(drc_index);
5021a8061c4SNathan Fontenot 	if (rc) {
5031a8061c4SNathan Fontenot 		of_node_put(dn);
5046dedcca6SToshi Kani 		return rc;
5051a8061c4SNathan Fontenot 	}
5061a8061c4SNathan Fontenot 
5071a8061c4SNathan Fontenot 	rc = dlpar_detach_node(dn);
5081a8061c4SNathan Fontenot 	if (rc) {
509e36d1227SThomas Falcon 		dlpar_acquire_drc(drc_index);
5106dedcca6SToshi Kani 		return rc;
5111a8061c4SNathan Fontenot 	}
5121a8061c4SNathan Fontenot 
5131a8061c4SNathan Fontenot 	of_node_put(dn);
5146dedcca6SToshi Kani 
5156dedcca6SToshi Kani 	return count;
5161a8061c4SNathan Fontenot }
5171a8061c4SNathan Fontenot 
518999e2dadSNathan Fontenot #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */
519999e2dadSNathan Fontenot 
520999e2dadSNathan Fontenot static int handle_dlpar_errorlog(struct pseries_hp_errorlog *hp_elog)
521999e2dadSNathan Fontenot {
522999e2dadSNathan Fontenot 	int rc;
523999e2dadSNathan Fontenot 
524999e2dadSNathan Fontenot 	/* pseries error logs are in BE format, convert to cpu type */
525999e2dadSNathan Fontenot 	switch (hp_elog->id_type) {
526999e2dadSNathan Fontenot 	case PSERIES_HP_ELOG_ID_DRC_COUNT:
527999e2dadSNathan Fontenot 		hp_elog->_drc_u.drc_count =
528999e2dadSNathan Fontenot 					be32_to_cpu(hp_elog->_drc_u.drc_count);
529999e2dadSNathan Fontenot 		break;
530999e2dadSNathan Fontenot 	case PSERIES_HP_ELOG_ID_DRC_INDEX:
531999e2dadSNathan Fontenot 		hp_elog->_drc_u.drc_index =
532999e2dadSNathan Fontenot 					be32_to_cpu(hp_elog->_drc_u.drc_index);
533999e2dadSNathan Fontenot 	}
534999e2dadSNathan Fontenot 
535999e2dadSNathan Fontenot 	switch (hp_elog->resource) {
536999e2dadSNathan Fontenot 	case PSERIES_HP_ELOG_RESOURCE_MEM:
537999e2dadSNathan Fontenot 		rc = dlpar_memory(hp_elog);
538999e2dadSNathan Fontenot 		break;
539999e2dadSNathan Fontenot 	default:
540999e2dadSNathan Fontenot 		pr_warn_ratelimited("Invalid resource (%d) specified\n",
541999e2dadSNathan Fontenot 				    hp_elog->resource);
542999e2dadSNathan Fontenot 		rc = -EINVAL;
543999e2dadSNathan Fontenot 	}
544999e2dadSNathan Fontenot 
545999e2dadSNathan Fontenot 	return rc;
546999e2dadSNathan Fontenot }
547999e2dadSNathan Fontenot 
548999e2dadSNathan Fontenot static ssize_t dlpar_store(struct class *class, struct class_attribute *attr,
549999e2dadSNathan Fontenot 			   const char *buf, size_t count)
550999e2dadSNathan Fontenot {
551999e2dadSNathan Fontenot 	struct pseries_hp_errorlog *hp_elog;
552999e2dadSNathan Fontenot 	const char *arg;
553999e2dadSNathan Fontenot 	int rc;
554999e2dadSNathan Fontenot 
555999e2dadSNathan Fontenot 	hp_elog = kzalloc(sizeof(*hp_elog), GFP_KERNEL);
556999e2dadSNathan Fontenot 	if (!hp_elog) {
557999e2dadSNathan Fontenot 		rc = -ENOMEM;
558999e2dadSNathan Fontenot 		goto dlpar_store_out;
559999e2dadSNathan Fontenot 	}
560999e2dadSNathan Fontenot 
561999e2dadSNathan Fontenot 	/* Parse out the request from the user, this will be in the form
562999e2dadSNathan Fontenot 	 * <resource> <action> <id_type> <id>
563999e2dadSNathan Fontenot 	 */
564999e2dadSNathan Fontenot 	arg = buf;
565999e2dadSNathan Fontenot 	if (!strncmp(arg, "memory", 6)) {
566999e2dadSNathan Fontenot 		hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_MEM;
567999e2dadSNathan Fontenot 		arg += strlen("memory ");
568999e2dadSNathan Fontenot 	} else {
569999e2dadSNathan Fontenot 		pr_err("Invalid resource specified: \"%s\"\n", buf);
570999e2dadSNathan Fontenot 		rc = -EINVAL;
571999e2dadSNathan Fontenot 		goto dlpar_store_out;
572999e2dadSNathan Fontenot 	}
573999e2dadSNathan Fontenot 
574999e2dadSNathan Fontenot 	if (!strncmp(arg, "add", 3)) {
575999e2dadSNathan Fontenot 		hp_elog->action = PSERIES_HP_ELOG_ACTION_ADD;
576999e2dadSNathan Fontenot 		arg += strlen("add ");
577999e2dadSNathan Fontenot 	} else if (!strncmp(arg, "remove", 6)) {
578999e2dadSNathan Fontenot 		hp_elog->action = PSERIES_HP_ELOG_ACTION_REMOVE;
579999e2dadSNathan Fontenot 		arg += strlen("remove ");
580999e2dadSNathan Fontenot 	} else {
581999e2dadSNathan Fontenot 		pr_err("Invalid action specified: \"%s\"\n", buf);
582999e2dadSNathan Fontenot 		rc = -EINVAL;
583999e2dadSNathan Fontenot 		goto dlpar_store_out;
584999e2dadSNathan Fontenot 	}
585999e2dadSNathan Fontenot 
586999e2dadSNathan Fontenot 	if (!strncmp(arg, "index", 5)) {
587999e2dadSNathan Fontenot 		u32 index;
588999e2dadSNathan Fontenot 
589999e2dadSNathan Fontenot 		hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_INDEX;
590999e2dadSNathan Fontenot 		arg += strlen("index ");
591999e2dadSNathan Fontenot 		if (kstrtou32(arg, 0, &index)) {
592999e2dadSNathan Fontenot 			rc = -EINVAL;
593999e2dadSNathan Fontenot 			pr_err("Invalid drc_index specified: \"%s\"\n", buf);
594999e2dadSNathan Fontenot 			goto dlpar_store_out;
595999e2dadSNathan Fontenot 		}
596999e2dadSNathan Fontenot 
597999e2dadSNathan Fontenot 		hp_elog->_drc_u.drc_index = cpu_to_be32(index);
598999e2dadSNathan Fontenot 	} else if (!strncmp(arg, "count", 5)) {
599999e2dadSNathan Fontenot 		u32 count;
600999e2dadSNathan Fontenot 
601999e2dadSNathan Fontenot 		hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_COUNT;
602999e2dadSNathan Fontenot 		arg += strlen("count ");
603999e2dadSNathan Fontenot 		if (kstrtou32(arg, 0, &count)) {
604999e2dadSNathan Fontenot 			rc = -EINVAL;
605999e2dadSNathan Fontenot 			pr_err("Invalid count specified: \"%s\"\n", buf);
606999e2dadSNathan Fontenot 			goto dlpar_store_out;
607999e2dadSNathan Fontenot 		}
608999e2dadSNathan Fontenot 
609999e2dadSNathan Fontenot 		hp_elog->_drc_u.drc_count = cpu_to_be32(count);
610999e2dadSNathan Fontenot 	} else {
611999e2dadSNathan Fontenot 		pr_err("Invalid id_type specified: \"%s\"\n", buf);
612999e2dadSNathan Fontenot 		rc = -EINVAL;
613999e2dadSNathan Fontenot 		goto dlpar_store_out;
614999e2dadSNathan Fontenot 	}
615999e2dadSNathan Fontenot 
616999e2dadSNathan Fontenot 	rc = handle_dlpar_errorlog(hp_elog);
617999e2dadSNathan Fontenot 
618999e2dadSNathan Fontenot dlpar_store_out:
619999e2dadSNathan Fontenot 	kfree(hp_elog);
620999e2dadSNathan Fontenot 	return rc ? rc : count;
621999e2dadSNathan Fontenot }
622999e2dadSNathan Fontenot 
623999e2dadSNathan Fontenot static CLASS_ATTR(dlpar, S_IWUSR, NULL, dlpar_store);
624999e2dadSNathan Fontenot 
6251a8061c4SNathan Fontenot static int __init pseries_dlpar_init(void)
6261a8061c4SNathan Fontenot {
627999e2dadSNathan Fontenot 	int rc;
628999e2dadSNathan Fontenot 
629999e2dadSNathan Fontenot #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
6301a8061c4SNathan Fontenot 	ppc_md.cpu_probe = dlpar_cpu_probe;
6311a8061c4SNathan Fontenot 	ppc_md.cpu_release = dlpar_cpu_release;
632999e2dadSNathan Fontenot #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */
6331a8061c4SNathan Fontenot 
634999e2dadSNathan Fontenot 	rc = sysfs_create_file(kernel_kobj, &class_attr_dlpar.attr);
635999e2dadSNathan Fontenot 
636999e2dadSNathan Fontenot 	return rc;
6371a8061c4SNathan Fontenot }
6381a8061c4SNathan Fontenot machine_device_initcall(pseries, pseries_dlpar_init);
6391a8061c4SNathan Fontenot 
640