xref: /OK3568_Linux_fs/kernel/arch/powerpc/platforms/pseries/dlpar.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Support for dynamic reconfiguration for PCI, Memory, and CPU
4*4882a593Smuzhiyun  * Hotplug and Dynamic Logical Partitioning on RPA platforms.
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * Copyright (C) 2009 Nathan Fontenot
7*4882a593Smuzhiyun  * Copyright (C) 2009 IBM Corporation
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #define pr_fmt(fmt)	"dlpar: " fmt
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <linux/kernel.h>
13*4882a593Smuzhiyun #include <linux/notifier.h>
14*4882a593Smuzhiyun #include <linux/spinlock.h>
15*4882a593Smuzhiyun #include <linux/cpu.h>
16*4882a593Smuzhiyun #include <linux/slab.h>
17*4882a593Smuzhiyun #include <linux/of.h>
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun #include "of_helpers.h"
20*4882a593Smuzhiyun #include "pseries.h"
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun #include <asm/prom.h>
23*4882a593Smuzhiyun #include <asm/machdep.h>
24*4882a593Smuzhiyun #include <linux/uaccess.h>
25*4882a593Smuzhiyun #include <asm/rtas.h>
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun static struct workqueue_struct *pseries_hp_wq;
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun struct pseries_hp_work {
30*4882a593Smuzhiyun 	struct work_struct work;
31*4882a593Smuzhiyun 	struct pseries_hp_errorlog *errlog;
32*4882a593Smuzhiyun };
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun struct cc_workarea {
35*4882a593Smuzhiyun 	__be32	drc_index;
36*4882a593Smuzhiyun 	__be32	zero;
37*4882a593Smuzhiyun 	__be32	name_offset;
38*4882a593Smuzhiyun 	__be32	prop_length;
39*4882a593Smuzhiyun 	__be32	prop_offset;
40*4882a593Smuzhiyun };
41*4882a593Smuzhiyun 
dlpar_free_cc_property(struct property * prop)42*4882a593Smuzhiyun void dlpar_free_cc_property(struct property *prop)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun 	kfree(prop->name);
45*4882a593Smuzhiyun 	kfree(prop->value);
46*4882a593Smuzhiyun 	kfree(prop);
47*4882a593Smuzhiyun }
48*4882a593Smuzhiyun 
dlpar_parse_cc_property(struct cc_workarea * ccwa)49*4882a593Smuzhiyun static struct property *dlpar_parse_cc_property(struct cc_workarea *ccwa)
50*4882a593Smuzhiyun {
51*4882a593Smuzhiyun 	struct property *prop;
52*4882a593Smuzhiyun 	char *name;
53*4882a593Smuzhiyun 	char *value;
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun 	prop = kzalloc(sizeof(*prop), GFP_KERNEL);
56*4882a593Smuzhiyun 	if (!prop)
57*4882a593Smuzhiyun 		return NULL;
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun 	name = (char *)ccwa + be32_to_cpu(ccwa->name_offset);
60*4882a593Smuzhiyun 	prop->name = kstrdup(name, GFP_KERNEL);
61*4882a593Smuzhiyun 	if (!prop->name) {
62*4882a593Smuzhiyun 		dlpar_free_cc_property(prop);
63*4882a593Smuzhiyun 		return NULL;
64*4882a593Smuzhiyun 	}
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	prop->length = be32_to_cpu(ccwa->prop_length);
67*4882a593Smuzhiyun 	value = (char *)ccwa + be32_to_cpu(ccwa->prop_offset);
68*4882a593Smuzhiyun 	prop->value = kmemdup(value, prop->length, GFP_KERNEL);
69*4882a593Smuzhiyun 	if (!prop->value) {
70*4882a593Smuzhiyun 		dlpar_free_cc_property(prop);
71*4882a593Smuzhiyun 		return NULL;
72*4882a593Smuzhiyun 	}
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	return prop;
75*4882a593Smuzhiyun }
76*4882a593Smuzhiyun 
dlpar_parse_cc_node(struct cc_workarea * ccwa)77*4882a593Smuzhiyun static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa)
78*4882a593Smuzhiyun {
79*4882a593Smuzhiyun 	struct device_node *dn;
80*4882a593Smuzhiyun 	const char *name;
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 	dn = kzalloc(sizeof(*dn), GFP_KERNEL);
83*4882a593Smuzhiyun 	if (!dn)
84*4882a593Smuzhiyun 		return NULL;
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 	name = (const char *)ccwa + be32_to_cpu(ccwa->name_offset);
87*4882a593Smuzhiyun 	dn->full_name = kstrdup(name, GFP_KERNEL);
88*4882a593Smuzhiyun 	if (!dn->full_name) {
89*4882a593Smuzhiyun 		kfree(dn);
90*4882a593Smuzhiyun 		return NULL;
91*4882a593Smuzhiyun 	}
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun 	of_node_set_flag(dn, OF_DYNAMIC);
94*4882a593Smuzhiyun 	of_node_init(dn);
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 	return dn;
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun 
dlpar_free_one_cc_node(struct device_node * dn)99*4882a593Smuzhiyun static void dlpar_free_one_cc_node(struct device_node *dn)
100*4882a593Smuzhiyun {
101*4882a593Smuzhiyun 	struct property *prop;
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun 	while (dn->properties) {
104*4882a593Smuzhiyun 		prop = dn->properties;
105*4882a593Smuzhiyun 		dn->properties = prop->next;
106*4882a593Smuzhiyun 		dlpar_free_cc_property(prop);
107*4882a593Smuzhiyun 	}
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	kfree(dn->full_name);
110*4882a593Smuzhiyun 	kfree(dn);
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun 
dlpar_free_cc_nodes(struct device_node * dn)113*4882a593Smuzhiyun void dlpar_free_cc_nodes(struct device_node *dn)
114*4882a593Smuzhiyun {
115*4882a593Smuzhiyun 	if (dn->child)
116*4882a593Smuzhiyun 		dlpar_free_cc_nodes(dn->child);
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	if (dn->sibling)
119*4882a593Smuzhiyun 		dlpar_free_cc_nodes(dn->sibling);
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	dlpar_free_one_cc_node(dn);
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun #define COMPLETE	0
125*4882a593Smuzhiyun #define NEXT_SIBLING    1
126*4882a593Smuzhiyun #define NEXT_CHILD      2
127*4882a593Smuzhiyun #define NEXT_PROPERTY   3
128*4882a593Smuzhiyun #define PREV_PARENT     4
129*4882a593Smuzhiyun #define MORE_MEMORY     5
130*4882a593Smuzhiyun #define ERR_CFG_USE     -9003
131*4882a593Smuzhiyun 
dlpar_configure_connector(__be32 drc_index,struct device_node * parent)132*4882a593Smuzhiyun struct device_node *dlpar_configure_connector(__be32 drc_index,
133*4882a593Smuzhiyun 					      struct device_node *parent)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun 	struct device_node *dn;
136*4882a593Smuzhiyun 	struct device_node *first_dn = NULL;
137*4882a593Smuzhiyun 	struct device_node *last_dn = NULL;
138*4882a593Smuzhiyun 	struct property *property;
139*4882a593Smuzhiyun 	struct property *last_property = NULL;
140*4882a593Smuzhiyun 	struct cc_workarea *ccwa;
141*4882a593Smuzhiyun 	char *data_buf;
142*4882a593Smuzhiyun 	int cc_token;
143*4882a593Smuzhiyun 	int rc = -1;
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	cc_token = rtas_token("ibm,configure-connector");
146*4882a593Smuzhiyun 	if (cc_token == RTAS_UNKNOWN_SERVICE)
147*4882a593Smuzhiyun 		return NULL;
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun 	data_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
150*4882a593Smuzhiyun 	if (!data_buf)
151*4882a593Smuzhiyun 		return NULL;
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	ccwa = (struct cc_workarea *)&data_buf[0];
154*4882a593Smuzhiyun 	ccwa->drc_index = drc_index;
155*4882a593Smuzhiyun 	ccwa->zero = 0;
156*4882a593Smuzhiyun 
157*4882a593Smuzhiyun 	do {
158*4882a593Smuzhiyun 		/* Since we release the rtas_data_buf lock between configure
159*4882a593Smuzhiyun 		 * connector calls we want to re-populate the rtas_data_buffer
160*4882a593Smuzhiyun 		 * with the contents of the previous call.
161*4882a593Smuzhiyun 		 */
162*4882a593Smuzhiyun 		spin_lock(&rtas_data_buf_lock);
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 		memcpy(rtas_data_buf, data_buf, RTAS_DATA_BUF_SIZE);
165*4882a593Smuzhiyun 		rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL);
166*4882a593Smuzhiyun 		memcpy(data_buf, rtas_data_buf, RTAS_DATA_BUF_SIZE);
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 		spin_unlock(&rtas_data_buf_lock);
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 		if (rtas_busy_delay(rc))
171*4882a593Smuzhiyun 			continue;
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 		switch (rc) {
174*4882a593Smuzhiyun 		case COMPLETE:
175*4882a593Smuzhiyun 			break;
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 		case NEXT_SIBLING:
178*4882a593Smuzhiyun 			dn = dlpar_parse_cc_node(ccwa);
179*4882a593Smuzhiyun 			if (!dn)
180*4882a593Smuzhiyun 				goto cc_error;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 			dn->parent = last_dn->parent;
183*4882a593Smuzhiyun 			last_dn->sibling = dn;
184*4882a593Smuzhiyun 			last_dn = dn;
185*4882a593Smuzhiyun 			break;
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 		case NEXT_CHILD:
188*4882a593Smuzhiyun 			dn = dlpar_parse_cc_node(ccwa);
189*4882a593Smuzhiyun 			if (!dn)
190*4882a593Smuzhiyun 				goto cc_error;
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 			if (!first_dn) {
193*4882a593Smuzhiyun 				dn->parent = parent;
194*4882a593Smuzhiyun 				first_dn = dn;
195*4882a593Smuzhiyun 			} else {
196*4882a593Smuzhiyun 				dn->parent = last_dn;
197*4882a593Smuzhiyun 				if (last_dn)
198*4882a593Smuzhiyun 					last_dn->child = dn;
199*4882a593Smuzhiyun 			}
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 			last_dn = dn;
202*4882a593Smuzhiyun 			break;
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 		case NEXT_PROPERTY:
205*4882a593Smuzhiyun 			property = dlpar_parse_cc_property(ccwa);
206*4882a593Smuzhiyun 			if (!property)
207*4882a593Smuzhiyun 				goto cc_error;
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 			if (!last_dn->properties)
210*4882a593Smuzhiyun 				last_dn->properties = property;
211*4882a593Smuzhiyun 			else
212*4882a593Smuzhiyun 				last_property->next = property;
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 			last_property = property;
215*4882a593Smuzhiyun 			break;
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 		case PREV_PARENT:
218*4882a593Smuzhiyun 			last_dn = last_dn->parent;
219*4882a593Smuzhiyun 			break;
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 		case MORE_MEMORY:
222*4882a593Smuzhiyun 		case ERR_CFG_USE:
223*4882a593Smuzhiyun 		default:
224*4882a593Smuzhiyun 			printk(KERN_ERR "Unexpected Error (%d) "
225*4882a593Smuzhiyun 			       "returned from configure-connector\n", rc);
226*4882a593Smuzhiyun 			goto cc_error;
227*4882a593Smuzhiyun 		}
228*4882a593Smuzhiyun 	} while (rc);
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun cc_error:
231*4882a593Smuzhiyun 	kfree(data_buf);
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	if (rc) {
234*4882a593Smuzhiyun 		if (first_dn)
235*4882a593Smuzhiyun 			dlpar_free_cc_nodes(first_dn);
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 		return NULL;
238*4882a593Smuzhiyun 	}
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun 	return first_dn;
241*4882a593Smuzhiyun }
242*4882a593Smuzhiyun 
dlpar_attach_node(struct device_node * dn,struct device_node * parent)243*4882a593Smuzhiyun int dlpar_attach_node(struct device_node *dn, struct device_node *parent)
244*4882a593Smuzhiyun {
245*4882a593Smuzhiyun 	int rc;
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 	dn->parent = parent;
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 	rc = of_attach_node(dn);
250*4882a593Smuzhiyun 	if (rc) {
251*4882a593Smuzhiyun 		printk(KERN_ERR "Failed to add device node %pOF\n", dn);
252*4882a593Smuzhiyun 		return rc;
253*4882a593Smuzhiyun 	}
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 	return 0;
256*4882a593Smuzhiyun }
257*4882a593Smuzhiyun 
dlpar_detach_node(struct device_node * dn)258*4882a593Smuzhiyun int dlpar_detach_node(struct device_node *dn)
259*4882a593Smuzhiyun {
260*4882a593Smuzhiyun 	struct device_node *child;
261*4882a593Smuzhiyun 	int rc;
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 	child = of_get_next_child(dn, NULL);
264*4882a593Smuzhiyun 	while (child) {
265*4882a593Smuzhiyun 		dlpar_detach_node(child);
266*4882a593Smuzhiyun 		child = of_get_next_child(dn, child);
267*4882a593Smuzhiyun 	}
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	rc = of_detach_node(dn);
270*4882a593Smuzhiyun 	if (rc)
271*4882a593Smuzhiyun 		return rc;
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun 	of_node_put(dn);
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun 	return 0;
276*4882a593Smuzhiyun }
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun #define DR_ENTITY_SENSE		9003
279*4882a593Smuzhiyun #define DR_ENTITY_PRESENT	1
280*4882a593Smuzhiyun #define DR_ENTITY_UNUSABLE	2
281*4882a593Smuzhiyun #define ALLOCATION_STATE	9003
282*4882a593Smuzhiyun #define ALLOC_UNUSABLE		0
283*4882a593Smuzhiyun #define ALLOC_USABLE		1
284*4882a593Smuzhiyun #define ISOLATION_STATE		9001
285*4882a593Smuzhiyun #define ISOLATE			0
286*4882a593Smuzhiyun #define UNISOLATE		1
287*4882a593Smuzhiyun 
dlpar_acquire_drc(u32 drc_index)288*4882a593Smuzhiyun int dlpar_acquire_drc(u32 drc_index)
289*4882a593Smuzhiyun {
290*4882a593Smuzhiyun 	int dr_status, rc;
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
293*4882a593Smuzhiyun 		       DR_ENTITY_SENSE, drc_index);
294*4882a593Smuzhiyun 	if (rc || dr_status != DR_ENTITY_UNUSABLE)
295*4882a593Smuzhiyun 		return -1;
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun 	rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE);
298*4882a593Smuzhiyun 	if (rc)
299*4882a593Smuzhiyun 		return rc;
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 	rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
302*4882a593Smuzhiyun 	if (rc) {
303*4882a593Smuzhiyun 		rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
304*4882a593Smuzhiyun 		return rc;
305*4882a593Smuzhiyun 	}
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 	return 0;
308*4882a593Smuzhiyun }
309*4882a593Smuzhiyun 
dlpar_release_drc(u32 drc_index)310*4882a593Smuzhiyun int dlpar_release_drc(u32 drc_index)
311*4882a593Smuzhiyun {
312*4882a593Smuzhiyun 	int dr_status, rc;
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
315*4882a593Smuzhiyun 		       DR_ENTITY_SENSE, drc_index);
316*4882a593Smuzhiyun 	if (rc || dr_status != DR_ENTITY_PRESENT)
317*4882a593Smuzhiyun 		return -1;
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun 	rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE);
320*4882a593Smuzhiyun 	if (rc)
321*4882a593Smuzhiyun 		return rc;
322*4882a593Smuzhiyun 
323*4882a593Smuzhiyun 	rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
324*4882a593Smuzhiyun 	if (rc) {
325*4882a593Smuzhiyun 		rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
326*4882a593Smuzhiyun 		return rc;
327*4882a593Smuzhiyun 	}
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 	return 0;
330*4882a593Smuzhiyun }
331*4882a593Smuzhiyun 
handle_dlpar_errorlog(struct pseries_hp_errorlog * hp_elog)332*4882a593Smuzhiyun int handle_dlpar_errorlog(struct pseries_hp_errorlog *hp_elog)
333*4882a593Smuzhiyun {
334*4882a593Smuzhiyun 	int rc;
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 	/* pseries error logs are in BE format, convert to cpu type */
337*4882a593Smuzhiyun 	switch (hp_elog->id_type) {
338*4882a593Smuzhiyun 	case PSERIES_HP_ELOG_ID_DRC_COUNT:
339*4882a593Smuzhiyun 		hp_elog->_drc_u.drc_count =
340*4882a593Smuzhiyun 				be32_to_cpu(hp_elog->_drc_u.drc_count);
341*4882a593Smuzhiyun 		break;
342*4882a593Smuzhiyun 	case PSERIES_HP_ELOG_ID_DRC_INDEX:
343*4882a593Smuzhiyun 		hp_elog->_drc_u.drc_index =
344*4882a593Smuzhiyun 				be32_to_cpu(hp_elog->_drc_u.drc_index);
345*4882a593Smuzhiyun 		break;
346*4882a593Smuzhiyun 	case PSERIES_HP_ELOG_ID_DRC_IC:
347*4882a593Smuzhiyun 		hp_elog->_drc_u.ic.count =
348*4882a593Smuzhiyun 				be32_to_cpu(hp_elog->_drc_u.ic.count);
349*4882a593Smuzhiyun 		hp_elog->_drc_u.ic.index =
350*4882a593Smuzhiyun 				be32_to_cpu(hp_elog->_drc_u.ic.index);
351*4882a593Smuzhiyun 	}
352*4882a593Smuzhiyun 
353*4882a593Smuzhiyun 	switch (hp_elog->resource) {
354*4882a593Smuzhiyun 	case PSERIES_HP_ELOG_RESOURCE_MEM:
355*4882a593Smuzhiyun 		rc = dlpar_memory(hp_elog);
356*4882a593Smuzhiyun 		break;
357*4882a593Smuzhiyun 	case PSERIES_HP_ELOG_RESOURCE_CPU:
358*4882a593Smuzhiyun 		rc = dlpar_cpu(hp_elog);
359*4882a593Smuzhiyun 		break;
360*4882a593Smuzhiyun 	case PSERIES_HP_ELOG_RESOURCE_PMEM:
361*4882a593Smuzhiyun 		rc = dlpar_hp_pmem(hp_elog);
362*4882a593Smuzhiyun 		break;
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun 	default:
365*4882a593Smuzhiyun 		pr_warn_ratelimited("Invalid resource (%d) specified\n",
366*4882a593Smuzhiyun 				    hp_elog->resource);
367*4882a593Smuzhiyun 		rc = -EINVAL;
368*4882a593Smuzhiyun 	}
369*4882a593Smuzhiyun 
370*4882a593Smuzhiyun 	return rc;
371*4882a593Smuzhiyun }
372*4882a593Smuzhiyun 
pseries_hp_work_fn(struct work_struct * work)373*4882a593Smuzhiyun static void pseries_hp_work_fn(struct work_struct *work)
374*4882a593Smuzhiyun {
375*4882a593Smuzhiyun 	struct pseries_hp_work *hp_work =
376*4882a593Smuzhiyun 			container_of(work, struct pseries_hp_work, work);
377*4882a593Smuzhiyun 
378*4882a593Smuzhiyun 	handle_dlpar_errorlog(hp_work->errlog);
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun 	kfree(hp_work->errlog);
381*4882a593Smuzhiyun 	kfree((void *)work);
382*4882a593Smuzhiyun }
383*4882a593Smuzhiyun 
queue_hotplug_event(struct pseries_hp_errorlog * hp_errlog)384*4882a593Smuzhiyun void queue_hotplug_event(struct pseries_hp_errorlog *hp_errlog)
385*4882a593Smuzhiyun {
386*4882a593Smuzhiyun 	struct pseries_hp_work *work;
387*4882a593Smuzhiyun 	struct pseries_hp_errorlog *hp_errlog_copy;
388*4882a593Smuzhiyun 
389*4882a593Smuzhiyun 	hp_errlog_copy = kmemdup(hp_errlog, sizeof(*hp_errlog), GFP_ATOMIC);
390*4882a593Smuzhiyun 	if (!hp_errlog_copy)
391*4882a593Smuzhiyun 		return;
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun 	work = kmalloc(sizeof(struct pseries_hp_work), GFP_ATOMIC);
394*4882a593Smuzhiyun 	if (work) {
395*4882a593Smuzhiyun 		INIT_WORK((struct work_struct *)work, pseries_hp_work_fn);
396*4882a593Smuzhiyun 		work->errlog = hp_errlog_copy;
397*4882a593Smuzhiyun 		queue_work(pseries_hp_wq, (struct work_struct *)work);
398*4882a593Smuzhiyun 	} else {
399*4882a593Smuzhiyun 		kfree(hp_errlog_copy);
400*4882a593Smuzhiyun 	}
401*4882a593Smuzhiyun }
402*4882a593Smuzhiyun 
dlpar_parse_resource(char ** cmd,struct pseries_hp_errorlog * hp_elog)403*4882a593Smuzhiyun static int dlpar_parse_resource(char **cmd, struct pseries_hp_errorlog *hp_elog)
404*4882a593Smuzhiyun {
405*4882a593Smuzhiyun 	char *arg;
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun 	arg = strsep(cmd, " ");
408*4882a593Smuzhiyun 	if (!arg)
409*4882a593Smuzhiyun 		return -EINVAL;
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 	if (sysfs_streq(arg, "memory")) {
412*4882a593Smuzhiyun 		hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_MEM;
413*4882a593Smuzhiyun 	} else if (sysfs_streq(arg, "cpu")) {
414*4882a593Smuzhiyun 		hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_CPU;
415*4882a593Smuzhiyun 	} else {
416*4882a593Smuzhiyun 		pr_err("Invalid resource specified.\n");
417*4882a593Smuzhiyun 		return -EINVAL;
418*4882a593Smuzhiyun 	}
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	return 0;
421*4882a593Smuzhiyun }
422*4882a593Smuzhiyun 
dlpar_parse_action(char ** cmd,struct pseries_hp_errorlog * hp_elog)423*4882a593Smuzhiyun static int dlpar_parse_action(char **cmd, struct pseries_hp_errorlog *hp_elog)
424*4882a593Smuzhiyun {
425*4882a593Smuzhiyun 	char *arg;
426*4882a593Smuzhiyun 
427*4882a593Smuzhiyun 	arg = strsep(cmd, " ");
428*4882a593Smuzhiyun 	if (!arg)
429*4882a593Smuzhiyun 		return -EINVAL;
430*4882a593Smuzhiyun 
431*4882a593Smuzhiyun 	if (sysfs_streq(arg, "add")) {
432*4882a593Smuzhiyun 		hp_elog->action = PSERIES_HP_ELOG_ACTION_ADD;
433*4882a593Smuzhiyun 	} else if (sysfs_streq(arg, "remove")) {
434*4882a593Smuzhiyun 		hp_elog->action = PSERIES_HP_ELOG_ACTION_REMOVE;
435*4882a593Smuzhiyun 	} else {
436*4882a593Smuzhiyun 		pr_err("Invalid action specified.\n");
437*4882a593Smuzhiyun 		return -EINVAL;
438*4882a593Smuzhiyun 	}
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun 	return 0;
441*4882a593Smuzhiyun }
442*4882a593Smuzhiyun 
dlpar_parse_id_type(char ** cmd,struct pseries_hp_errorlog * hp_elog)443*4882a593Smuzhiyun static int dlpar_parse_id_type(char **cmd, struct pseries_hp_errorlog *hp_elog)
444*4882a593Smuzhiyun {
445*4882a593Smuzhiyun 	char *arg;
446*4882a593Smuzhiyun 	u32 count, index;
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 	arg = strsep(cmd, " ");
449*4882a593Smuzhiyun 	if (!arg)
450*4882a593Smuzhiyun 		return -EINVAL;
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun 	if (sysfs_streq(arg, "indexed-count")) {
453*4882a593Smuzhiyun 		hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_IC;
454*4882a593Smuzhiyun 		arg = strsep(cmd, " ");
455*4882a593Smuzhiyun 		if (!arg) {
456*4882a593Smuzhiyun 			pr_err("No DRC count specified.\n");
457*4882a593Smuzhiyun 			return -EINVAL;
458*4882a593Smuzhiyun 		}
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 		if (kstrtou32(arg, 0, &count)) {
461*4882a593Smuzhiyun 			pr_err("Invalid DRC count specified.\n");
462*4882a593Smuzhiyun 			return -EINVAL;
463*4882a593Smuzhiyun 		}
464*4882a593Smuzhiyun 
465*4882a593Smuzhiyun 		arg = strsep(cmd, " ");
466*4882a593Smuzhiyun 		if (!arg) {
467*4882a593Smuzhiyun 			pr_err("No DRC Index specified.\n");
468*4882a593Smuzhiyun 			return -EINVAL;
469*4882a593Smuzhiyun 		}
470*4882a593Smuzhiyun 
471*4882a593Smuzhiyun 		if (kstrtou32(arg, 0, &index)) {
472*4882a593Smuzhiyun 			pr_err("Invalid DRC Index specified.\n");
473*4882a593Smuzhiyun 			return -EINVAL;
474*4882a593Smuzhiyun 		}
475*4882a593Smuzhiyun 
476*4882a593Smuzhiyun 		hp_elog->_drc_u.ic.count = cpu_to_be32(count);
477*4882a593Smuzhiyun 		hp_elog->_drc_u.ic.index = cpu_to_be32(index);
478*4882a593Smuzhiyun 	} else if (sysfs_streq(arg, "index")) {
479*4882a593Smuzhiyun 		hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_INDEX;
480*4882a593Smuzhiyun 		arg = strsep(cmd, " ");
481*4882a593Smuzhiyun 		if (!arg) {
482*4882a593Smuzhiyun 			pr_err("No DRC Index specified.\n");
483*4882a593Smuzhiyun 			return -EINVAL;
484*4882a593Smuzhiyun 		}
485*4882a593Smuzhiyun 
486*4882a593Smuzhiyun 		if (kstrtou32(arg, 0, &index)) {
487*4882a593Smuzhiyun 			pr_err("Invalid DRC Index specified.\n");
488*4882a593Smuzhiyun 			return -EINVAL;
489*4882a593Smuzhiyun 		}
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun 		hp_elog->_drc_u.drc_index = cpu_to_be32(index);
492*4882a593Smuzhiyun 	} else if (sysfs_streq(arg, "count")) {
493*4882a593Smuzhiyun 		hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_COUNT;
494*4882a593Smuzhiyun 		arg = strsep(cmd, " ");
495*4882a593Smuzhiyun 		if (!arg) {
496*4882a593Smuzhiyun 			pr_err("No DRC count specified.\n");
497*4882a593Smuzhiyun 			return -EINVAL;
498*4882a593Smuzhiyun 		}
499*4882a593Smuzhiyun 
500*4882a593Smuzhiyun 		if (kstrtou32(arg, 0, &count)) {
501*4882a593Smuzhiyun 			pr_err("Invalid DRC count specified.\n");
502*4882a593Smuzhiyun 			return -EINVAL;
503*4882a593Smuzhiyun 		}
504*4882a593Smuzhiyun 
505*4882a593Smuzhiyun 		hp_elog->_drc_u.drc_count = cpu_to_be32(count);
506*4882a593Smuzhiyun 	} else {
507*4882a593Smuzhiyun 		pr_err("Invalid id_type specified.\n");
508*4882a593Smuzhiyun 		return -EINVAL;
509*4882a593Smuzhiyun 	}
510*4882a593Smuzhiyun 
511*4882a593Smuzhiyun 	return 0;
512*4882a593Smuzhiyun }
513*4882a593Smuzhiyun 
dlpar_store(struct class * class,struct class_attribute * attr,const char * buf,size_t count)514*4882a593Smuzhiyun static ssize_t dlpar_store(struct class *class, struct class_attribute *attr,
515*4882a593Smuzhiyun 			   const char *buf, size_t count)
516*4882a593Smuzhiyun {
517*4882a593Smuzhiyun 	struct pseries_hp_errorlog hp_elog;
518*4882a593Smuzhiyun 	char *argbuf;
519*4882a593Smuzhiyun 	char *args;
520*4882a593Smuzhiyun 	int rc;
521*4882a593Smuzhiyun 
522*4882a593Smuzhiyun 	args = argbuf = kstrdup(buf, GFP_KERNEL);
523*4882a593Smuzhiyun 	if (!argbuf) {
524*4882a593Smuzhiyun 		pr_info("Could not allocate resources for DLPAR operation\n");
525*4882a593Smuzhiyun 		kfree(argbuf);
526*4882a593Smuzhiyun 		return -ENOMEM;
527*4882a593Smuzhiyun 	}
528*4882a593Smuzhiyun 
529*4882a593Smuzhiyun 	/*
530*4882a593Smuzhiyun 	 * Parse out the request from the user, this will be in the form:
531*4882a593Smuzhiyun 	 * <resource> <action> <id_type> <id>
532*4882a593Smuzhiyun 	 */
533*4882a593Smuzhiyun 	rc = dlpar_parse_resource(&args, &hp_elog);
534*4882a593Smuzhiyun 	if (rc)
535*4882a593Smuzhiyun 		goto dlpar_store_out;
536*4882a593Smuzhiyun 
537*4882a593Smuzhiyun 	rc = dlpar_parse_action(&args, &hp_elog);
538*4882a593Smuzhiyun 	if (rc)
539*4882a593Smuzhiyun 		goto dlpar_store_out;
540*4882a593Smuzhiyun 
541*4882a593Smuzhiyun 	rc = dlpar_parse_id_type(&args, &hp_elog);
542*4882a593Smuzhiyun 	if (rc)
543*4882a593Smuzhiyun 		goto dlpar_store_out;
544*4882a593Smuzhiyun 
545*4882a593Smuzhiyun 	rc = handle_dlpar_errorlog(&hp_elog);
546*4882a593Smuzhiyun 
547*4882a593Smuzhiyun dlpar_store_out:
548*4882a593Smuzhiyun 	kfree(argbuf);
549*4882a593Smuzhiyun 
550*4882a593Smuzhiyun 	if (rc)
551*4882a593Smuzhiyun 		pr_err("Could not handle DLPAR request \"%s\"\n", buf);
552*4882a593Smuzhiyun 
553*4882a593Smuzhiyun 	return rc ? rc : count;
554*4882a593Smuzhiyun }
555*4882a593Smuzhiyun 
dlpar_show(struct class * class,struct class_attribute * attr,char * buf)556*4882a593Smuzhiyun static ssize_t dlpar_show(struct class *class, struct class_attribute *attr,
557*4882a593Smuzhiyun 			  char *buf)
558*4882a593Smuzhiyun {
559*4882a593Smuzhiyun 	return sprintf(buf, "%s\n", "memory,cpu");
560*4882a593Smuzhiyun }
561*4882a593Smuzhiyun 
562*4882a593Smuzhiyun static CLASS_ATTR_RW(dlpar);
563*4882a593Smuzhiyun 
dlpar_workqueue_init(void)564*4882a593Smuzhiyun int __init dlpar_workqueue_init(void)
565*4882a593Smuzhiyun {
566*4882a593Smuzhiyun 	if (pseries_hp_wq)
567*4882a593Smuzhiyun 		return 0;
568*4882a593Smuzhiyun 
569*4882a593Smuzhiyun 	pseries_hp_wq = alloc_workqueue("pseries hotplug workqueue",
570*4882a593Smuzhiyun 			WQ_UNBOUND, 1);
571*4882a593Smuzhiyun 
572*4882a593Smuzhiyun 	return pseries_hp_wq ? 0 : -ENOMEM;
573*4882a593Smuzhiyun }
574*4882a593Smuzhiyun 
dlpar_sysfs_init(void)575*4882a593Smuzhiyun static int __init dlpar_sysfs_init(void)
576*4882a593Smuzhiyun {
577*4882a593Smuzhiyun 	int rc;
578*4882a593Smuzhiyun 
579*4882a593Smuzhiyun 	rc = dlpar_workqueue_init();
580*4882a593Smuzhiyun 	if (rc)
581*4882a593Smuzhiyun 		return rc;
582*4882a593Smuzhiyun 
583*4882a593Smuzhiyun 	return sysfs_create_file(kernel_kobj, &class_attr_dlpar.attr);
584*4882a593Smuzhiyun }
585*4882a593Smuzhiyun machine_device_initcall(pseries, dlpar_sysfs_init);
586*4882a593Smuzhiyun 
587