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