xref: /openbmc/linux/arch/powerpc/platforms/pseries/dlpar.c (revision ab519a011caa5ec47d992cb8a4fc8e7af9b9e3f8)
1*ab519a01SNathan Fontenot /*
2*ab519a01SNathan Fontenot  * Support for dynamic reconfiguration for PCI, Memory, and CPU
3*ab519a01SNathan Fontenot  * Hotplug and Dynamic Logical Partitioning on RPA platforms.
4*ab519a01SNathan Fontenot  *
5*ab519a01SNathan Fontenot  * Copyright (C) 2009 Nathan Fontenot
6*ab519a01SNathan Fontenot  * Copyright (C) 2009 IBM Corporation
7*ab519a01SNathan Fontenot  *
8*ab519a01SNathan Fontenot  * This program is free software; you can redistribute it and/or
9*ab519a01SNathan Fontenot  * modify it under the terms of the GNU General Public License version
10*ab519a01SNathan Fontenot  * 2 as published by the Free Software Foundation.
11*ab519a01SNathan Fontenot  */
12*ab519a01SNathan Fontenot 
13*ab519a01SNathan Fontenot #include <linux/kernel.h>
14*ab519a01SNathan Fontenot #include <linux/kref.h>
15*ab519a01SNathan Fontenot #include <linux/notifier.h>
16*ab519a01SNathan Fontenot #include <linux/proc_fs.h>
17*ab519a01SNathan Fontenot #include <linux/spinlock.h>
18*ab519a01SNathan Fontenot #include <linux/cpu.h>
19*ab519a01SNathan Fontenot 
20*ab519a01SNathan Fontenot #include <asm/prom.h>
21*ab519a01SNathan Fontenot #include <asm/machdep.h>
22*ab519a01SNathan Fontenot #include <asm/uaccess.h>
23*ab519a01SNathan Fontenot #include <asm/rtas.h>
24*ab519a01SNathan Fontenot #include <asm/pSeries_reconfig.h>
25*ab519a01SNathan Fontenot 
26*ab519a01SNathan Fontenot struct cc_workarea {
27*ab519a01SNathan Fontenot 	u32	drc_index;
28*ab519a01SNathan Fontenot 	u32	zero;
29*ab519a01SNathan Fontenot 	u32	name_offset;
30*ab519a01SNathan Fontenot 	u32	prop_length;
31*ab519a01SNathan Fontenot 	u32	prop_offset;
32*ab519a01SNathan Fontenot };
33*ab519a01SNathan Fontenot 
34*ab519a01SNathan Fontenot static void dlpar_free_cc_property(struct property *prop)
35*ab519a01SNathan Fontenot {
36*ab519a01SNathan Fontenot 	kfree(prop->name);
37*ab519a01SNathan Fontenot 	kfree(prop->value);
38*ab519a01SNathan Fontenot 	kfree(prop);
39*ab519a01SNathan Fontenot }
40*ab519a01SNathan Fontenot 
41*ab519a01SNathan Fontenot static struct property *dlpar_parse_cc_property(struct cc_workarea *ccwa)
42*ab519a01SNathan Fontenot {
43*ab519a01SNathan Fontenot 	struct property *prop;
44*ab519a01SNathan Fontenot 	char *name;
45*ab519a01SNathan Fontenot 	char *value;
46*ab519a01SNathan Fontenot 
47*ab519a01SNathan Fontenot 	prop = kzalloc(sizeof(*prop), GFP_KERNEL);
48*ab519a01SNathan Fontenot 	if (!prop)
49*ab519a01SNathan Fontenot 		return NULL;
50*ab519a01SNathan Fontenot 
51*ab519a01SNathan Fontenot 	name = (char *)ccwa + ccwa->name_offset;
52*ab519a01SNathan Fontenot 	prop->name = kstrdup(name, GFP_KERNEL);
53*ab519a01SNathan Fontenot 
54*ab519a01SNathan Fontenot 	prop->length = ccwa->prop_length;
55*ab519a01SNathan Fontenot 	value = (char *)ccwa + ccwa->prop_offset;
56*ab519a01SNathan Fontenot 	prop->value = kzalloc(prop->length, GFP_KERNEL);
57*ab519a01SNathan Fontenot 	if (!prop->value) {
58*ab519a01SNathan Fontenot 		dlpar_free_cc_property(prop);
59*ab519a01SNathan Fontenot 		return NULL;
60*ab519a01SNathan Fontenot 	}
61*ab519a01SNathan Fontenot 
62*ab519a01SNathan Fontenot 	memcpy(prop->value, value, prop->length);
63*ab519a01SNathan Fontenot 	return prop;
64*ab519a01SNathan Fontenot }
65*ab519a01SNathan Fontenot 
66*ab519a01SNathan Fontenot static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa)
67*ab519a01SNathan Fontenot {
68*ab519a01SNathan Fontenot 	struct device_node *dn;
69*ab519a01SNathan Fontenot 	char *name;
70*ab519a01SNathan Fontenot 
71*ab519a01SNathan Fontenot 	dn = kzalloc(sizeof(*dn), GFP_KERNEL);
72*ab519a01SNathan Fontenot 	if (!dn)
73*ab519a01SNathan Fontenot 		return NULL;
74*ab519a01SNathan Fontenot 
75*ab519a01SNathan Fontenot 	/* The configure connector reported name does not contain a
76*ab519a01SNathan Fontenot 	 * preceeding '/', so we allocate a buffer large enough to
77*ab519a01SNathan Fontenot 	 * prepend this to the full_name.
78*ab519a01SNathan Fontenot 	 */
79*ab519a01SNathan Fontenot 	name = (char *)ccwa + ccwa->name_offset;
80*ab519a01SNathan Fontenot 	dn->full_name = kmalloc(strlen(name) + 2, GFP_KERNEL);
81*ab519a01SNathan Fontenot 	if (!dn->full_name) {
82*ab519a01SNathan Fontenot 		kfree(dn);
83*ab519a01SNathan Fontenot 		return NULL;
84*ab519a01SNathan Fontenot 	}
85*ab519a01SNathan Fontenot 
86*ab519a01SNathan Fontenot 	sprintf(dn->full_name, "/%s", name);
87*ab519a01SNathan Fontenot 	return dn;
88*ab519a01SNathan Fontenot }
89*ab519a01SNathan Fontenot 
90*ab519a01SNathan Fontenot static void dlpar_free_one_cc_node(struct device_node *dn)
91*ab519a01SNathan Fontenot {
92*ab519a01SNathan Fontenot 	struct property *prop;
93*ab519a01SNathan Fontenot 
94*ab519a01SNathan Fontenot 	while (dn->properties) {
95*ab519a01SNathan Fontenot 		prop = dn->properties;
96*ab519a01SNathan Fontenot 		dn->properties = prop->next;
97*ab519a01SNathan Fontenot 		dlpar_free_cc_property(prop);
98*ab519a01SNathan Fontenot 	}
99*ab519a01SNathan Fontenot 
100*ab519a01SNathan Fontenot 	kfree(dn->full_name);
101*ab519a01SNathan Fontenot 	kfree(dn);
102*ab519a01SNathan Fontenot }
103*ab519a01SNathan Fontenot 
104*ab519a01SNathan Fontenot static void dlpar_free_cc_nodes(struct device_node *dn)
105*ab519a01SNathan Fontenot {
106*ab519a01SNathan Fontenot 	if (dn->child)
107*ab519a01SNathan Fontenot 		dlpar_free_cc_nodes(dn->child);
108*ab519a01SNathan Fontenot 
109*ab519a01SNathan Fontenot 	if (dn->sibling)
110*ab519a01SNathan Fontenot 		dlpar_free_cc_nodes(dn->sibling);
111*ab519a01SNathan Fontenot 
112*ab519a01SNathan Fontenot 	dlpar_free_one_cc_node(dn);
113*ab519a01SNathan Fontenot }
114*ab519a01SNathan Fontenot 
115*ab519a01SNathan Fontenot #define NEXT_SIBLING    1
116*ab519a01SNathan Fontenot #define NEXT_CHILD      2
117*ab519a01SNathan Fontenot #define NEXT_PROPERTY   3
118*ab519a01SNathan Fontenot #define PREV_PARENT     4
119*ab519a01SNathan Fontenot #define MORE_MEMORY     5
120*ab519a01SNathan Fontenot #define CALL_AGAIN	-2
121*ab519a01SNathan Fontenot #define ERR_CFG_USE     -9003
122*ab519a01SNathan Fontenot 
123*ab519a01SNathan Fontenot struct device_node *dlpar_configure_connector(u32 drc_index)
124*ab519a01SNathan Fontenot {
125*ab519a01SNathan Fontenot 	struct device_node *dn;
126*ab519a01SNathan Fontenot 	struct device_node *first_dn = NULL;
127*ab519a01SNathan Fontenot 	struct device_node *last_dn = NULL;
128*ab519a01SNathan Fontenot 	struct property *property;
129*ab519a01SNathan Fontenot 	struct property *last_property = NULL;
130*ab519a01SNathan Fontenot 	struct cc_workarea *ccwa;
131*ab519a01SNathan Fontenot 	int cc_token;
132*ab519a01SNathan Fontenot 	int rc;
133*ab519a01SNathan Fontenot 
134*ab519a01SNathan Fontenot 	cc_token = rtas_token("ibm,configure-connector");
135*ab519a01SNathan Fontenot 	if (cc_token == RTAS_UNKNOWN_SERVICE)
136*ab519a01SNathan Fontenot 		return NULL;
137*ab519a01SNathan Fontenot 
138*ab519a01SNathan Fontenot 	spin_lock(&rtas_data_buf_lock);
139*ab519a01SNathan Fontenot 	ccwa = (struct cc_workarea *)&rtas_data_buf[0];
140*ab519a01SNathan Fontenot 	ccwa->drc_index = drc_index;
141*ab519a01SNathan Fontenot 	ccwa->zero = 0;
142*ab519a01SNathan Fontenot 
143*ab519a01SNathan Fontenot 	rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL);
144*ab519a01SNathan Fontenot 	while (rc) {
145*ab519a01SNathan Fontenot 		switch (rc) {
146*ab519a01SNathan Fontenot 		case NEXT_SIBLING:
147*ab519a01SNathan Fontenot 			dn = dlpar_parse_cc_node(ccwa);
148*ab519a01SNathan Fontenot 			if (!dn)
149*ab519a01SNathan Fontenot 				goto cc_error;
150*ab519a01SNathan Fontenot 
151*ab519a01SNathan Fontenot 			dn->parent = last_dn->parent;
152*ab519a01SNathan Fontenot 			last_dn->sibling = dn;
153*ab519a01SNathan Fontenot 			last_dn = dn;
154*ab519a01SNathan Fontenot 			break;
155*ab519a01SNathan Fontenot 
156*ab519a01SNathan Fontenot 		case NEXT_CHILD:
157*ab519a01SNathan Fontenot 			dn = dlpar_parse_cc_node(ccwa);
158*ab519a01SNathan Fontenot 			if (!dn)
159*ab519a01SNathan Fontenot 				goto cc_error;
160*ab519a01SNathan Fontenot 
161*ab519a01SNathan Fontenot 			if (!first_dn)
162*ab519a01SNathan Fontenot 				first_dn = dn;
163*ab519a01SNathan Fontenot 			else {
164*ab519a01SNathan Fontenot 				dn->parent = last_dn;
165*ab519a01SNathan Fontenot 				if (last_dn)
166*ab519a01SNathan Fontenot 					last_dn->child = dn;
167*ab519a01SNathan Fontenot 			}
168*ab519a01SNathan Fontenot 
169*ab519a01SNathan Fontenot 			last_dn = dn;
170*ab519a01SNathan Fontenot 			break;
171*ab519a01SNathan Fontenot 
172*ab519a01SNathan Fontenot 		case NEXT_PROPERTY:
173*ab519a01SNathan Fontenot 			property = dlpar_parse_cc_property(ccwa);
174*ab519a01SNathan Fontenot 			if (!property)
175*ab519a01SNathan Fontenot 				goto cc_error;
176*ab519a01SNathan Fontenot 
177*ab519a01SNathan Fontenot 			if (!last_dn->properties)
178*ab519a01SNathan Fontenot 				last_dn->properties = property;
179*ab519a01SNathan Fontenot 			else
180*ab519a01SNathan Fontenot 				last_property->next = property;
181*ab519a01SNathan Fontenot 
182*ab519a01SNathan Fontenot 			last_property = property;
183*ab519a01SNathan Fontenot 			break;
184*ab519a01SNathan Fontenot 
185*ab519a01SNathan Fontenot 		case PREV_PARENT:
186*ab519a01SNathan Fontenot 			last_dn = last_dn->parent;
187*ab519a01SNathan Fontenot 			break;
188*ab519a01SNathan Fontenot 
189*ab519a01SNathan Fontenot 		case CALL_AGAIN:
190*ab519a01SNathan Fontenot 			break;
191*ab519a01SNathan Fontenot 
192*ab519a01SNathan Fontenot 		case MORE_MEMORY:
193*ab519a01SNathan Fontenot 		case ERR_CFG_USE:
194*ab519a01SNathan Fontenot 		default:
195*ab519a01SNathan Fontenot 			printk(KERN_ERR "Unexpected Error (%d) "
196*ab519a01SNathan Fontenot 			       "returned from configure-connector\n", rc);
197*ab519a01SNathan Fontenot 			goto cc_error;
198*ab519a01SNathan Fontenot 		}
199*ab519a01SNathan Fontenot 
200*ab519a01SNathan Fontenot 		rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL);
201*ab519a01SNathan Fontenot 	}
202*ab519a01SNathan Fontenot 
203*ab519a01SNathan Fontenot 	spin_unlock(&rtas_data_buf_lock);
204*ab519a01SNathan Fontenot 	return first_dn;
205*ab519a01SNathan Fontenot 
206*ab519a01SNathan Fontenot cc_error:
207*ab519a01SNathan Fontenot 	if (first_dn)
208*ab519a01SNathan Fontenot 		dlpar_free_cc_nodes(first_dn);
209*ab519a01SNathan Fontenot 	spin_unlock(&rtas_data_buf_lock);
210*ab519a01SNathan Fontenot 	return NULL;
211*ab519a01SNathan Fontenot }
212*ab519a01SNathan Fontenot 
213*ab519a01SNathan Fontenot static struct device_node *derive_parent(const char *path)
214*ab519a01SNathan Fontenot {
215*ab519a01SNathan Fontenot 	struct device_node *parent;
216*ab519a01SNathan Fontenot 	char *last_slash;
217*ab519a01SNathan Fontenot 
218*ab519a01SNathan Fontenot 	last_slash = strrchr(path, '/');
219*ab519a01SNathan Fontenot 	if (last_slash == path) {
220*ab519a01SNathan Fontenot 		parent = of_find_node_by_path("/");
221*ab519a01SNathan Fontenot 	} else {
222*ab519a01SNathan Fontenot 		char *parent_path;
223*ab519a01SNathan Fontenot 		int parent_path_len = last_slash - path + 1;
224*ab519a01SNathan Fontenot 		parent_path = kmalloc(parent_path_len, GFP_KERNEL);
225*ab519a01SNathan Fontenot 		if (!parent_path)
226*ab519a01SNathan Fontenot 			return NULL;
227*ab519a01SNathan Fontenot 
228*ab519a01SNathan Fontenot 		strlcpy(parent_path, path, parent_path_len);
229*ab519a01SNathan Fontenot 		parent = of_find_node_by_path(parent_path);
230*ab519a01SNathan Fontenot 		kfree(parent_path);
231*ab519a01SNathan Fontenot 	}
232*ab519a01SNathan Fontenot 
233*ab519a01SNathan Fontenot 	return parent;
234*ab519a01SNathan Fontenot }
235*ab519a01SNathan Fontenot 
236*ab519a01SNathan Fontenot int dlpar_attach_node(struct device_node *dn)
237*ab519a01SNathan Fontenot {
238*ab519a01SNathan Fontenot 	struct proc_dir_entry *ent;
239*ab519a01SNathan Fontenot 	int rc;
240*ab519a01SNathan Fontenot 
241*ab519a01SNathan Fontenot 	of_node_set_flag(dn, OF_DYNAMIC);
242*ab519a01SNathan Fontenot 	kref_init(&dn->kref);
243*ab519a01SNathan Fontenot 	dn->parent = derive_parent(dn->full_name);
244*ab519a01SNathan Fontenot 	if (!dn->parent)
245*ab519a01SNathan Fontenot 		return -ENOMEM;
246*ab519a01SNathan Fontenot 
247*ab519a01SNathan Fontenot 	rc = blocking_notifier_call_chain(&pSeries_reconfig_chain,
248*ab519a01SNathan Fontenot 					  PSERIES_RECONFIG_ADD, dn);
249*ab519a01SNathan Fontenot 	if (rc == NOTIFY_BAD) {
250*ab519a01SNathan Fontenot 		printk(KERN_ERR "Failed to add device node %s\n",
251*ab519a01SNathan Fontenot 		       dn->full_name);
252*ab519a01SNathan Fontenot 		return -ENOMEM; /* For now, safe to assume kmalloc failure */
253*ab519a01SNathan Fontenot 	}
254*ab519a01SNathan Fontenot 
255*ab519a01SNathan Fontenot 	of_attach_node(dn);
256*ab519a01SNathan Fontenot 
257*ab519a01SNathan Fontenot #ifdef CONFIG_PROC_DEVICETREE
258*ab519a01SNathan Fontenot 	ent = proc_mkdir(strrchr(dn->full_name, '/') + 1, dn->parent->pde);
259*ab519a01SNathan Fontenot 	if (ent)
260*ab519a01SNathan Fontenot 		proc_device_tree_add_node(dn, ent);
261*ab519a01SNathan Fontenot #endif
262*ab519a01SNathan Fontenot 
263*ab519a01SNathan Fontenot 	of_node_put(dn->parent);
264*ab519a01SNathan Fontenot 	return 0;
265*ab519a01SNathan Fontenot }
266*ab519a01SNathan Fontenot 
267*ab519a01SNathan Fontenot int dlpar_detach_node(struct device_node *dn)
268*ab519a01SNathan Fontenot {
269*ab519a01SNathan Fontenot 	struct device_node *parent = dn->parent;
270*ab519a01SNathan Fontenot 	struct property *prop = dn->properties;
271*ab519a01SNathan Fontenot 
272*ab519a01SNathan Fontenot #ifdef CONFIG_PROC_DEVICETREE
273*ab519a01SNathan Fontenot 	while (prop) {
274*ab519a01SNathan Fontenot 		remove_proc_entry(prop->name, dn->pde);
275*ab519a01SNathan Fontenot 		prop = prop->next;
276*ab519a01SNathan Fontenot 	}
277*ab519a01SNathan Fontenot 
278*ab519a01SNathan Fontenot 	if (dn->pde)
279*ab519a01SNathan Fontenot 		remove_proc_entry(dn->pde->name, parent->pde);
280*ab519a01SNathan Fontenot #endif
281*ab519a01SNathan Fontenot 
282*ab519a01SNathan Fontenot 	blocking_notifier_call_chain(&pSeries_reconfig_chain,
283*ab519a01SNathan Fontenot 			    PSERIES_RECONFIG_REMOVE, dn);
284*ab519a01SNathan Fontenot 	of_detach_node(dn);
285*ab519a01SNathan Fontenot 	of_node_put(dn); /* Must decrement the refcount */
286*ab519a01SNathan Fontenot 
287*ab519a01SNathan Fontenot 	return 0;
288*ab519a01SNathan Fontenot }
289*ab519a01SNathan Fontenot 
290*ab519a01SNathan Fontenot #define DR_ENTITY_SENSE		9003
291*ab519a01SNathan Fontenot #define DR_ENTITY_PRESENT	1
292*ab519a01SNathan Fontenot #define DR_ENTITY_UNUSABLE	2
293*ab519a01SNathan Fontenot #define ALLOCATION_STATE	9003
294*ab519a01SNathan Fontenot #define ALLOC_UNUSABLE		0
295*ab519a01SNathan Fontenot #define ALLOC_USABLE		1
296*ab519a01SNathan Fontenot #define ISOLATION_STATE		9001
297*ab519a01SNathan Fontenot #define ISOLATE			0
298*ab519a01SNathan Fontenot #define UNISOLATE		1
299*ab519a01SNathan Fontenot 
300*ab519a01SNathan Fontenot int dlpar_acquire_drc(u32 drc_index)
301*ab519a01SNathan Fontenot {
302*ab519a01SNathan Fontenot 	int dr_status, rc;
303*ab519a01SNathan Fontenot 
304*ab519a01SNathan Fontenot 	rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
305*ab519a01SNathan Fontenot 		       DR_ENTITY_SENSE, drc_index);
306*ab519a01SNathan Fontenot 	if (rc || dr_status != DR_ENTITY_UNUSABLE)
307*ab519a01SNathan Fontenot 		return -1;
308*ab519a01SNathan Fontenot 
309*ab519a01SNathan Fontenot 	rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE);
310*ab519a01SNathan Fontenot 	if (rc)
311*ab519a01SNathan Fontenot 		return rc;
312*ab519a01SNathan Fontenot 
313*ab519a01SNathan Fontenot 	rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
314*ab519a01SNathan Fontenot 	if (rc) {
315*ab519a01SNathan Fontenot 		rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
316*ab519a01SNathan Fontenot 		return rc;
317*ab519a01SNathan Fontenot 	}
318*ab519a01SNathan Fontenot 
319*ab519a01SNathan Fontenot 	return 0;
320*ab519a01SNathan Fontenot }
321*ab519a01SNathan Fontenot 
322*ab519a01SNathan Fontenot int dlpar_release_drc(u32 drc_index)
323*ab519a01SNathan Fontenot {
324*ab519a01SNathan Fontenot 	int dr_status, rc;
325*ab519a01SNathan Fontenot 
326*ab519a01SNathan Fontenot 	rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
327*ab519a01SNathan Fontenot 		       DR_ENTITY_SENSE, drc_index);
328*ab519a01SNathan Fontenot 	if (rc || dr_status != DR_ENTITY_PRESENT)
329*ab519a01SNathan Fontenot 		return -1;
330*ab519a01SNathan Fontenot 
331*ab519a01SNathan Fontenot 	rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE);
332*ab519a01SNathan Fontenot 	if (rc)
333*ab519a01SNathan Fontenot 		return rc;
334*ab519a01SNathan Fontenot 
335*ab519a01SNathan Fontenot 	rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
336*ab519a01SNathan Fontenot 	if (rc) {
337*ab519a01SNathan Fontenot 		rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
338*ab519a01SNathan Fontenot 		return rc;
339*ab519a01SNathan Fontenot 	}
340*ab519a01SNathan Fontenot 
341*ab519a01SNathan Fontenot 	return 0;
342*ab519a01SNathan Fontenot }
343*ab519a01SNathan Fontenot 
344*ab519a01SNathan Fontenot 
345