1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2ab519a01SNathan Fontenot /* 3ab519a01SNathan Fontenot * Support for dynamic reconfiguration for PCI, Memory, and CPU 4ab519a01SNathan Fontenot * Hotplug and Dynamic Logical Partitioning on RPA platforms. 5ab519a01SNathan Fontenot * 6ab519a01SNathan Fontenot * Copyright (C) 2009 Nathan Fontenot 7ab519a01SNathan Fontenot * Copyright (C) 2009 IBM Corporation 8ab519a01SNathan Fontenot */ 9ab519a01SNathan Fontenot 10999e2dadSNathan Fontenot #define pr_fmt(fmt) "dlpar: " fmt 11999e2dadSNathan Fontenot 12ab519a01SNathan Fontenot #include <linux/kernel.h> 13ab519a01SNathan Fontenot #include <linux/notifier.h> 14ab519a01SNathan Fontenot #include <linux/spinlock.h> 15ab519a01SNathan Fontenot #include <linux/cpu.h> 165a0e3ad6STejun Heo #include <linux/slab.h> 171cf3d8b3SNathan Fontenot #include <linux/of.h> 1806bacefcSAndy Shevchenko 1906bacefcSAndy Shevchenko #include "of_helpers.h" 201217d34bSAnton Blanchard #include "pseries.h" 21ab519a01SNathan Fontenot 22ab519a01SNathan Fontenot #include <asm/machdep.h> 237c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 24ab519a01SNathan Fontenot #include <asm/rtas.h> 25*e27e1423SNathan Lynch #include <asm/rtas-work-area.h> 26ab519a01SNathan Fontenot 277c98bd72SDaniel Axtens static struct workqueue_struct *pseries_hp_wq; 289054619eSJohn Allen 299054619eSJohn Allen struct pseries_hp_work { 309054619eSJohn Allen struct work_struct work; 319054619eSJohn Allen struct pseries_hp_errorlog *errlog; 329054619eSJohn Allen }; 339054619eSJohn Allen 34ab519a01SNathan Fontenot struct cc_workarea { 35d6f1e7abSBharata B Rao __be32 drc_index; 36d6f1e7abSBharata B Rao __be32 zero; 37d6f1e7abSBharata B Rao __be32 name_offset; 38d6f1e7abSBharata B Rao __be32 prop_length; 39d6f1e7abSBharata B Rao __be32 prop_offset; 40ab519a01SNathan Fontenot }; 41ab519a01SNathan Fontenot 4220648974SNathan Fontenot void dlpar_free_cc_property(struct property *prop) 43ab519a01SNathan Fontenot { 44ab519a01SNathan Fontenot kfree(prop->name); 45ab519a01SNathan Fontenot kfree(prop->value); 46ab519a01SNathan Fontenot kfree(prop); 47ab519a01SNathan Fontenot } 48ab519a01SNathan Fontenot 49ab519a01SNathan Fontenot static struct property *dlpar_parse_cc_property(struct cc_workarea *ccwa) 50ab519a01SNathan Fontenot { 51ab519a01SNathan Fontenot struct property *prop; 52ab519a01SNathan Fontenot char *name; 53ab519a01SNathan Fontenot char *value; 54ab519a01SNathan Fontenot 55ab519a01SNathan Fontenot prop = kzalloc(sizeof(*prop), GFP_KERNEL); 56ab519a01SNathan Fontenot if (!prop) 57ab519a01SNathan Fontenot return NULL; 58ab519a01SNathan Fontenot 59d6f1e7abSBharata B Rao name = (char *)ccwa + be32_to_cpu(ccwa->name_offset); 60ab519a01SNathan Fontenot prop->name = kstrdup(name, GFP_KERNEL); 61efa9ace6SGen Zhang if (!prop->name) { 62efa9ace6SGen Zhang dlpar_free_cc_property(prop); 63efa9ace6SGen Zhang return NULL; 64efa9ace6SGen Zhang } 65ab519a01SNathan Fontenot 66d6f1e7abSBharata B Rao prop->length = be32_to_cpu(ccwa->prop_length); 67d6f1e7abSBharata B Rao value = (char *)ccwa + be32_to_cpu(ccwa->prop_offset); 68e72ed6b5SNishanth Aravamudan prop->value = kmemdup(value, prop->length, GFP_KERNEL); 69ab519a01SNathan Fontenot if (!prop->value) { 70ab519a01SNathan Fontenot dlpar_free_cc_property(prop); 71ab519a01SNathan Fontenot return NULL; 72ab519a01SNathan Fontenot } 73ab519a01SNathan Fontenot 74ab519a01SNathan Fontenot return prop; 75ab519a01SNathan Fontenot } 76ab519a01SNathan Fontenot 7706665989SRob Herring static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa) 78ab519a01SNathan Fontenot { 79ab519a01SNathan Fontenot struct device_node *dn; 8006665989SRob Herring const char *name; 818d5ff320STyrel Datwyler 82ab519a01SNathan Fontenot dn = kzalloc(sizeof(*dn), GFP_KERNEL); 83ab519a01SNathan Fontenot if (!dn) 84ab519a01SNathan Fontenot return NULL; 85ab519a01SNathan Fontenot 8606665989SRob Herring name = (const char *)ccwa + be32_to_cpu(ccwa->name_offset); 8706665989SRob Herring dn->full_name = kstrdup(name, GFP_KERNEL); 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 ERR_CFG_USE -9003 131ab519a01SNathan Fontenot 132d6f1e7abSBharata B Rao struct device_node *dlpar_configure_connector(__be32 drc_index, 1338d5ff320STyrel Datwyler struct device_node *parent) 134ab519a01SNathan Fontenot { 135ab519a01SNathan Fontenot struct device_node *dn; 136ab519a01SNathan Fontenot struct device_node *first_dn = NULL; 137ab519a01SNathan Fontenot struct device_node *last_dn = NULL; 138ab519a01SNathan Fontenot struct property *property; 139ab519a01SNathan Fontenot struct property *last_property = NULL; 140ab519a01SNathan Fontenot struct cc_workarea *ccwa; 141*e27e1423SNathan Lynch struct rtas_work_area *work_area; 14293f68f1eSNathan Fontenot char *data_buf; 143ab519a01SNathan Fontenot int cc_token; 14493f68f1eSNathan Fontenot int rc = -1; 145ab519a01SNathan Fontenot 146ab519a01SNathan Fontenot cc_token = rtas_token("ibm,configure-connector"); 147ab519a01SNathan Fontenot if (cc_token == RTAS_UNKNOWN_SERVICE) 148ab519a01SNathan Fontenot return NULL; 149ab519a01SNathan Fontenot 150*e27e1423SNathan Lynch work_area = rtas_work_area_alloc(SZ_4K); 151*e27e1423SNathan Lynch data_buf = rtas_work_area_raw_buf(work_area); 15293f68f1eSNathan Fontenot 15393f68f1eSNathan Fontenot ccwa = (struct cc_workarea *)&data_buf[0]; 154ab519a01SNathan Fontenot ccwa->drc_index = drc_index; 155ab519a01SNathan Fontenot ccwa->zero = 0; 156ab519a01SNathan Fontenot 15793f68f1eSNathan Fontenot do { 158*e27e1423SNathan Lynch do { 159*e27e1423SNathan Lynch rc = rtas_call(cc_token, 2, 1, NULL, 160*e27e1423SNathan Lynch rtas_work_area_phys(work_area), NULL); 161*e27e1423SNathan Lynch } while (rtas_busy_delay(rc)); 162768d70e1SNathan Lynch 163ab519a01SNathan Fontenot switch (rc) { 1649c740025SAnton Blanchard case COMPLETE: 1659c740025SAnton Blanchard break; 1669c740025SAnton Blanchard 167ab519a01SNathan Fontenot case NEXT_SIBLING: 16806665989SRob Herring dn = dlpar_parse_cc_node(ccwa); 169ab519a01SNathan Fontenot if (!dn) 170ab519a01SNathan Fontenot goto cc_error; 171ab519a01SNathan Fontenot 172ab519a01SNathan Fontenot dn->parent = last_dn->parent; 173ab519a01SNathan Fontenot last_dn->sibling = dn; 174ab519a01SNathan Fontenot last_dn = dn; 175ab519a01SNathan Fontenot break; 176ab519a01SNathan Fontenot 177ab519a01SNathan Fontenot case NEXT_CHILD: 17806665989SRob Herring dn = dlpar_parse_cc_node(ccwa); 179ab519a01SNathan Fontenot if (!dn) 180ab519a01SNathan Fontenot goto cc_error; 181ab519a01SNathan Fontenot 1828d5ff320STyrel Datwyler if (!first_dn) { 1838d5ff320STyrel Datwyler dn->parent = parent; 184ab519a01SNathan Fontenot first_dn = dn; 1858d5ff320STyrel Datwyler } else { 186ab519a01SNathan Fontenot dn->parent = last_dn; 187ab519a01SNathan Fontenot if (last_dn) 188ab519a01SNathan Fontenot last_dn->child = dn; 189ab519a01SNathan Fontenot } 190ab519a01SNathan Fontenot 191ab519a01SNathan Fontenot last_dn = dn; 192ab519a01SNathan Fontenot break; 193ab519a01SNathan Fontenot 194ab519a01SNathan Fontenot case NEXT_PROPERTY: 195ab519a01SNathan Fontenot property = dlpar_parse_cc_property(ccwa); 196ab519a01SNathan Fontenot if (!property) 197ab519a01SNathan Fontenot goto cc_error; 198ab519a01SNathan Fontenot 199ab519a01SNathan Fontenot if (!last_dn->properties) 200ab519a01SNathan Fontenot last_dn->properties = property; 201ab519a01SNathan Fontenot else 202ab519a01SNathan Fontenot last_property->next = property; 203ab519a01SNathan Fontenot 204ab519a01SNathan Fontenot last_property = property; 205ab519a01SNathan Fontenot break; 206ab519a01SNathan Fontenot 207ab519a01SNathan Fontenot case PREV_PARENT: 208ab519a01SNathan Fontenot last_dn = last_dn->parent; 209ab519a01SNathan Fontenot break; 210ab519a01SNathan Fontenot 211ab519a01SNathan Fontenot case MORE_MEMORY: 212ab519a01SNathan Fontenot case ERR_CFG_USE: 213ab519a01SNathan Fontenot default: 214ab519a01SNathan Fontenot printk(KERN_ERR "Unexpected Error (%d) " 215ab519a01SNathan Fontenot "returned from configure-connector\n", rc); 216ab519a01SNathan Fontenot goto cc_error; 217ab519a01SNathan Fontenot } 21893f68f1eSNathan Fontenot } while (rc); 219ab519a01SNathan Fontenot 220ab519a01SNathan Fontenot cc_error: 221*e27e1423SNathan Lynch rtas_work_area_free(work_area); 22293f68f1eSNathan Fontenot 22393f68f1eSNathan Fontenot if (rc) { 224ab519a01SNathan Fontenot if (first_dn) 225ab519a01SNathan Fontenot dlpar_free_cc_nodes(first_dn); 22693f68f1eSNathan Fontenot 227ab519a01SNathan Fontenot return NULL; 228ab519a01SNathan Fontenot } 229ab519a01SNathan Fontenot 23093f68f1eSNathan Fontenot return first_dn; 23193f68f1eSNathan Fontenot } 23293f68f1eSNathan Fontenot 233215ee763SRob Herring int dlpar_attach_node(struct device_node *dn, struct device_node *parent) 234ab519a01SNathan Fontenot { 235ab519a01SNathan Fontenot int rc; 236ab519a01SNathan Fontenot 237215ee763SRob Herring dn->parent = parent; 238ab519a01SNathan Fontenot 2391cf3d8b3SNathan Fontenot rc = of_attach_node(dn); 2403aef19f0SAkinobu Mita if (rc) { 241b7c670d6SRob Herring printk(KERN_ERR "Failed to add device node %pOF\n", dn); 2423aef19f0SAkinobu Mita return rc; 243ab519a01SNathan Fontenot } 244ab519a01SNathan Fontenot 245ab519a01SNathan Fontenot return 0; 246ab519a01SNathan Fontenot } 247ab519a01SNathan Fontenot 248ab519a01SNathan Fontenot int dlpar_detach_node(struct device_node *dn) 249ab519a01SNathan Fontenot { 2505935ff43STyrel Datwyler struct device_node *child; 2511cf3d8b3SNathan Fontenot int rc; 252ab519a01SNathan Fontenot 2535935ff43STyrel Datwyler child = of_get_next_child(dn, NULL); 2545935ff43STyrel Datwyler while (child) { 2555935ff43STyrel Datwyler dlpar_detach_node(child); 2565935ff43STyrel Datwyler child = of_get_next_child(dn, child); 2575935ff43STyrel Datwyler } 2585935ff43STyrel Datwyler 2591cf3d8b3SNathan Fontenot rc = of_detach_node(dn); 2601cf3d8b3SNathan Fontenot if (rc) 2611cf3d8b3SNathan Fontenot return rc; 2621cf3d8b3SNathan Fontenot 2635b3f5c40SFrank Rowand of_node_put(dn); 2645b3f5c40SFrank Rowand 265ab519a01SNathan Fontenot return 0; 266ab519a01SNathan Fontenot } 267ab519a01SNathan Fontenot 268ab519a01SNathan Fontenot #define DR_ENTITY_SENSE 9003 269ab519a01SNathan Fontenot #define DR_ENTITY_PRESENT 1 270ab519a01SNathan Fontenot #define DR_ENTITY_UNUSABLE 2 271ab519a01SNathan Fontenot #define ALLOCATION_STATE 9003 272ab519a01SNathan Fontenot #define ALLOC_UNUSABLE 0 273ab519a01SNathan Fontenot #define ALLOC_USABLE 1 274ab519a01SNathan Fontenot #define ISOLATION_STATE 9001 275ab519a01SNathan Fontenot #define ISOLATE 0 276ab519a01SNathan Fontenot #define UNISOLATE 1 277ab519a01SNathan Fontenot 278ab519a01SNathan Fontenot int dlpar_acquire_drc(u32 drc_index) 279ab519a01SNathan Fontenot { 280ab519a01SNathan Fontenot int dr_status, rc; 281ab519a01SNathan Fontenot 282bfb0c9fcSNathan Lynch rc = rtas_get_sensor(DR_ENTITY_SENSE, drc_index, &dr_status); 283ab519a01SNathan Fontenot if (rc || dr_status != DR_ENTITY_UNUSABLE) 284ab519a01SNathan Fontenot return -1; 285ab519a01SNathan Fontenot 286ab519a01SNathan Fontenot rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE); 287ab519a01SNathan Fontenot if (rc) 288ab519a01SNathan Fontenot return rc; 289ab519a01SNathan Fontenot 290ab519a01SNathan Fontenot rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 291ab519a01SNathan Fontenot if (rc) { 292ab519a01SNathan Fontenot rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 293ab519a01SNathan Fontenot return rc; 294ab519a01SNathan Fontenot } 295ab519a01SNathan Fontenot 296ab519a01SNathan Fontenot return 0; 297ab519a01SNathan Fontenot } 298ab519a01SNathan Fontenot 299ab519a01SNathan Fontenot int dlpar_release_drc(u32 drc_index) 300ab519a01SNathan Fontenot { 301ab519a01SNathan Fontenot int dr_status, rc; 302ab519a01SNathan Fontenot 303bfb0c9fcSNathan Lynch rc = rtas_get_sensor(DR_ENTITY_SENSE, drc_index, &dr_status); 304ab519a01SNathan Fontenot if (rc || dr_status != DR_ENTITY_PRESENT) 305ab519a01SNathan Fontenot return -1; 306ab519a01SNathan Fontenot 307ab519a01SNathan Fontenot rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE); 308ab519a01SNathan Fontenot if (rc) 309ab519a01SNathan Fontenot return rc; 310ab519a01SNathan Fontenot 311ab519a01SNathan Fontenot rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 312ab519a01SNathan Fontenot if (rc) { 313ab519a01SNathan Fontenot rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 314ab519a01SNathan Fontenot return rc; 315ab519a01SNathan Fontenot } 316ab519a01SNathan Fontenot 317ab519a01SNathan Fontenot return 0; 318ab519a01SNathan Fontenot } 319ab519a01SNathan Fontenot 3200e3b3ff8SDaniel Henrique Barboza int dlpar_unisolate_drc(u32 drc_index) 3210e3b3ff8SDaniel Henrique Barboza { 3220e3b3ff8SDaniel Henrique Barboza int dr_status, rc; 3230e3b3ff8SDaniel Henrique Barboza 324bfb0c9fcSNathan Lynch rc = rtas_get_sensor(DR_ENTITY_SENSE, drc_index, &dr_status); 3250e3b3ff8SDaniel Henrique Barboza if (rc || dr_status != DR_ENTITY_PRESENT) 3260e3b3ff8SDaniel Henrique Barboza return -1; 3270e3b3ff8SDaniel Henrique Barboza 3280e3b3ff8SDaniel Henrique Barboza rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 3290e3b3ff8SDaniel Henrique Barboza 3300e3b3ff8SDaniel Henrique Barboza return 0; 3310e3b3ff8SDaniel Henrique Barboza } 3320e3b3ff8SDaniel Henrique Barboza 333fd12527aSNathan Fontenot int handle_dlpar_errorlog(struct pseries_hp_errorlog *hp_elog) 334999e2dadSNathan Fontenot { 335999e2dadSNathan Fontenot int rc; 336999e2dadSNathan Fontenot 337999e2dadSNathan Fontenot /* pseries error logs are in BE format, convert to cpu type */ 338999e2dadSNathan Fontenot switch (hp_elog->id_type) { 339999e2dadSNathan Fontenot case PSERIES_HP_ELOG_ID_DRC_COUNT: 340999e2dadSNathan Fontenot hp_elog->_drc_u.drc_count = 341999e2dadSNathan Fontenot be32_to_cpu(hp_elog->_drc_u.drc_count); 342999e2dadSNathan Fontenot break; 343999e2dadSNathan Fontenot case PSERIES_HP_ELOG_ID_DRC_INDEX: 344999e2dadSNathan Fontenot hp_elog->_drc_u.drc_index = 345999e2dadSNathan Fontenot be32_to_cpu(hp_elog->_drc_u.drc_index); 346333f7b76SSahil Mehta break; 347333f7b76SSahil Mehta case PSERIES_HP_ELOG_ID_DRC_IC: 348333f7b76SSahil Mehta hp_elog->_drc_u.ic.count = 349333f7b76SSahil Mehta be32_to_cpu(hp_elog->_drc_u.ic.count); 350333f7b76SSahil Mehta hp_elog->_drc_u.ic.index = 351333f7b76SSahil Mehta be32_to_cpu(hp_elog->_drc_u.ic.index); 352999e2dadSNathan Fontenot } 353999e2dadSNathan Fontenot 354999e2dadSNathan Fontenot switch (hp_elog->resource) { 355999e2dadSNathan Fontenot case PSERIES_HP_ELOG_RESOURCE_MEM: 356999e2dadSNathan Fontenot rc = dlpar_memory(hp_elog); 357999e2dadSNathan Fontenot break; 358e9d764f8SNathan Fontenot case PSERIES_HP_ELOG_RESOURCE_CPU: 359e9d764f8SNathan Fontenot rc = dlpar_cpu(hp_elog); 360e9d764f8SNathan Fontenot break; 3614c5d87dbSOliver O'Halloran case PSERIES_HP_ELOG_RESOURCE_PMEM: 3624c5d87dbSOliver O'Halloran rc = dlpar_hp_pmem(hp_elog); 3634c5d87dbSOliver O'Halloran break; 3644c5d87dbSOliver O'Halloran 365999e2dadSNathan Fontenot default: 366999e2dadSNathan Fontenot pr_warn_ratelimited("Invalid resource (%d) specified\n", 367999e2dadSNathan Fontenot hp_elog->resource); 368999e2dadSNathan Fontenot rc = -EINVAL; 369999e2dadSNathan Fontenot } 370999e2dadSNathan Fontenot 371999e2dadSNathan Fontenot return rc; 372999e2dadSNathan Fontenot } 373999e2dadSNathan Fontenot 3747c98bd72SDaniel Axtens static void pseries_hp_work_fn(struct work_struct *work) 3759054619eSJohn Allen { 3769054619eSJohn Allen struct pseries_hp_work *hp_work = 3779054619eSJohn Allen container_of(work, struct pseries_hp_work, work); 3789054619eSJohn Allen 3799054619eSJohn Allen handle_dlpar_errorlog(hp_work->errlog); 3809054619eSJohn Allen 3819054619eSJohn Allen kfree(hp_work->errlog); 382634a0b8fSXu Wang kfree(work); 3839054619eSJohn Allen } 3849054619eSJohn Allen 385fd12527aSNathan Fontenot void queue_hotplug_event(struct pseries_hp_errorlog *hp_errlog) 3869054619eSJohn Allen { 3879054619eSJohn Allen struct pseries_hp_work *work; 3889054619eSJohn Allen struct pseries_hp_errorlog *hp_errlog_copy; 3899054619eSJohn Allen 390348ea30fSNathan Lynch hp_errlog_copy = kmemdup(hp_errlog, sizeof(*hp_errlog), GFP_ATOMIC); 391348ea30fSNathan Lynch if (!hp_errlog_copy) 392348ea30fSNathan Lynch return; 3939054619eSJohn Allen 394348ea30fSNathan Lynch work = kmalloc(sizeof(struct pseries_hp_work), GFP_ATOMIC); 3959054619eSJohn Allen if (work) { 3969054619eSJohn Allen INIT_WORK((struct work_struct *)work, pseries_hp_work_fn); 3979054619eSJohn Allen work->errlog = hp_errlog_copy; 3989054619eSJohn Allen queue_work(pseries_hp_wq, (struct work_struct *)work); 3999054619eSJohn Allen } else { 40090ce3514SAndrew Donnellan kfree(hp_errlog_copy); 4019054619eSJohn Allen } 4029054619eSJohn Allen } 4039054619eSJohn Allen 40425b587fbSNathan Fontenot static int dlpar_parse_resource(char **cmd, struct pseries_hp_errorlog *hp_elog) 40525b587fbSNathan Fontenot { 40625b587fbSNathan Fontenot char *arg; 40725b587fbSNathan Fontenot 40825b587fbSNathan Fontenot arg = strsep(cmd, " "); 40925b587fbSNathan Fontenot if (!arg) 41025b587fbSNathan Fontenot return -EINVAL; 41125b587fbSNathan Fontenot 41225b587fbSNathan Fontenot if (sysfs_streq(arg, "memory")) { 41325b587fbSNathan Fontenot hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_MEM; 41425b587fbSNathan Fontenot } else if (sysfs_streq(arg, "cpu")) { 41525b587fbSNathan Fontenot hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_CPU; 41625b587fbSNathan Fontenot } else { 41725b587fbSNathan Fontenot pr_err("Invalid resource specified.\n"); 41825b587fbSNathan Fontenot return -EINVAL; 41925b587fbSNathan Fontenot } 42025b587fbSNathan Fontenot 42125b587fbSNathan Fontenot return 0; 42225b587fbSNathan Fontenot } 42325b587fbSNathan Fontenot 42425b587fbSNathan Fontenot static int dlpar_parse_action(char **cmd, struct pseries_hp_errorlog *hp_elog) 42525b587fbSNathan Fontenot { 42625b587fbSNathan Fontenot char *arg; 42725b587fbSNathan Fontenot 42825b587fbSNathan Fontenot arg = strsep(cmd, " "); 42925b587fbSNathan Fontenot if (!arg) 43025b587fbSNathan Fontenot return -EINVAL; 43125b587fbSNathan Fontenot 43225b587fbSNathan Fontenot if (sysfs_streq(arg, "add")) { 43325b587fbSNathan Fontenot hp_elog->action = PSERIES_HP_ELOG_ACTION_ADD; 43425b587fbSNathan Fontenot } else if (sysfs_streq(arg, "remove")) { 43525b587fbSNathan Fontenot hp_elog->action = PSERIES_HP_ELOG_ACTION_REMOVE; 43625b587fbSNathan Fontenot } else { 43725b587fbSNathan Fontenot pr_err("Invalid action specified.\n"); 43825b587fbSNathan Fontenot return -EINVAL; 43925b587fbSNathan Fontenot } 44025b587fbSNathan Fontenot 44125b587fbSNathan Fontenot return 0; 44225b587fbSNathan Fontenot } 44325b587fbSNathan Fontenot 44425b587fbSNathan Fontenot static int dlpar_parse_id_type(char **cmd, struct pseries_hp_errorlog *hp_elog) 44525b587fbSNathan Fontenot { 44625b587fbSNathan Fontenot char *arg; 44725b587fbSNathan Fontenot u32 count, index; 44825b587fbSNathan Fontenot 44925b587fbSNathan Fontenot arg = strsep(cmd, " "); 45025b587fbSNathan Fontenot if (!arg) 45125b587fbSNathan Fontenot return -EINVAL; 45225b587fbSNathan Fontenot 453333f7b76SSahil Mehta if (sysfs_streq(arg, "indexed-count")) { 454333f7b76SSahil Mehta hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_IC; 455333f7b76SSahil Mehta arg = strsep(cmd, " "); 456333f7b76SSahil Mehta if (!arg) { 457333f7b76SSahil Mehta pr_err("No DRC count specified.\n"); 458333f7b76SSahil Mehta return -EINVAL; 459333f7b76SSahil Mehta } 460333f7b76SSahil Mehta 461333f7b76SSahil Mehta if (kstrtou32(arg, 0, &count)) { 462333f7b76SSahil Mehta pr_err("Invalid DRC count specified.\n"); 463333f7b76SSahil Mehta return -EINVAL; 464333f7b76SSahil Mehta } 465333f7b76SSahil Mehta 466333f7b76SSahil Mehta arg = strsep(cmd, " "); 467333f7b76SSahil Mehta if (!arg) { 468333f7b76SSahil Mehta pr_err("No DRC Index specified.\n"); 469333f7b76SSahil Mehta return -EINVAL; 470333f7b76SSahil Mehta } 471333f7b76SSahil Mehta 472333f7b76SSahil Mehta if (kstrtou32(arg, 0, &index)) { 473333f7b76SSahil Mehta pr_err("Invalid DRC Index specified.\n"); 474333f7b76SSahil Mehta return -EINVAL; 475333f7b76SSahil Mehta } 476333f7b76SSahil Mehta 477333f7b76SSahil Mehta hp_elog->_drc_u.ic.count = cpu_to_be32(count); 478333f7b76SSahil Mehta hp_elog->_drc_u.ic.index = cpu_to_be32(index); 479333f7b76SSahil Mehta } else if (sysfs_streq(arg, "index")) { 48025b587fbSNathan Fontenot hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_INDEX; 48125b587fbSNathan Fontenot arg = strsep(cmd, " "); 48225b587fbSNathan Fontenot if (!arg) { 48325b587fbSNathan Fontenot pr_err("No DRC Index specified.\n"); 48425b587fbSNathan Fontenot return -EINVAL; 48525b587fbSNathan Fontenot } 48625b587fbSNathan Fontenot 48725b587fbSNathan Fontenot if (kstrtou32(arg, 0, &index)) { 48825b587fbSNathan Fontenot pr_err("Invalid DRC Index specified.\n"); 48925b587fbSNathan Fontenot return -EINVAL; 49025b587fbSNathan Fontenot } 49125b587fbSNathan Fontenot 49225b587fbSNathan Fontenot hp_elog->_drc_u.drc_index = cpu_to_be32(index); 49325b587fbSNathan Fontenot } else if (sysfs_streq(arg, "count")) { 49425b587fbSNathan Fontenot hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_COUNT; 49525b587fbSNathan Fontenot arg = strsep(cmd, " "); 49625b587fbSNathan Fontenot if (!arg) { 49725b587fbSNathan Fontenot pr_err("No DRC count specified.\n"); 49825b587fbSNathan Fontenot return -EINVAL; 49925b587fbSNathan Fontenot } 50025b587fbSNathan Fontenot 50125b587fbSNathan Fontenot if (kstrtou32(arg, 0, &count)) { 50225b587fbSNathan Fontenot pr_err("Invalid DRC count specified.\n"); 50325b587fbSNathan Fontenot return -EINVAL; 50425b587fbSNathan Fontenot } 50525b587fbSNathan Fontenot 50625b587fbSNathan Fontenot hp_elog->_drc_u.drc_count = cpu_to_be32(count); 50725b587fbSNathan Fontenot } else { 50825b587fbSNathan Fontenot pr_err("Invalid id_type specified.\n"); 50925b587fbSNathan Fontenot return -EINVAL; 51025b587fbSNathan Fontenot } 51125b587fbSNathan Fontenot 51225b587fbSNathan Fontenot return 0; 51325b587fbSNathan Fontenot } 51425b587fbSNathan Fontenot 515999e2dadSNathan Fontenot static ssize_t dlpar_store(struct class *class, struct class_attribute *attr, 516999e2dadSNathan Fontenot const char *buf, size_t count) 517999e2dadSNathan Fontenot { 518fd12527aSNathan Fontenot struct pseries_hp_errorlog hp_elog; 51925b587fbSNathan Fontenot char *argbuf; 52025b587fbSNathan Fontenot char *args; 521999e2dadSNathan Fontenot int rc; 522999e2dadSNathan Fontenot 52325b587fbSNathan Fontenot args = argbuf = kstrdup(buf, GFP_KERNEL); 5246e7a4da7SMarkus Elfring if (!argbuf) 52525b587fbSNathan Fontenot return -ENOMEM; 526999e2dadSNathan Fontenot 52725b587fbSNathan Fontenot /* 52825b587fbSNathan Fontenot * Parse out the request from the user, this will be in the form: 529999e2dadSNathan Fontenot * <resource> <action> <id_type> <id> 530999e2dadSNathan Fontenot */ 531fd12527aSNathan Fontenot rc = dlpar_parse_resource(&args, &hp_elog); 53225b587fbSNathan Fontenot if (rc) 533999e2dadSNathan Fontenot goto dlpar_store_out; 534999e2dadSNathan Fontenot 535fd12527aSNathan Fontenot rc = dlpar_parse_action(&args, &hp_elog); 53625b587fbSNathan Fontenot if (rc) 537999e2dadSNathan Fontenot goto dlpar_store_out; 538999e2dadSNathan Fontenot 539fd12527aSNathan Fontenot rc = dlpar_parse_id_type(&args, &hp_elog); 54025b587fbSNathan Fontenot if (rc) 541999e2dadSNathan Fontenot goto dlpar_store_out; 542999e2dadSNathan Fontenot 543fd12527aSNathan Fontenot rc = handle_dlpar_errorlog(&hp_elog); 544999e2dadSNathan Fontenot 545999e2dadSNathan Fontenot dlpar_store_out: 54625b587fbSNathan Fontenot kfree(argbuf); 54725b587fbSNathan Fontenot 54825b587fbSNathan Fontenot if (rc) 54925b587fbSNathan Fontenot pr_err("Could not handle DLPAR request \"%s\"\n", buf); 55025b587fbSNathan Fontenot 551999e2dadSNathan Fontenot return rc ? rc : count; 552999e2dadSNathan Fontenot } 553999e2dadSNathan Fontenot 554673bc435SNathan Fontenot static ssize_t dlpar_show(struct class *class, struct class_attribute *attr, 555673bc435SNathan Fontenot char *buf) 556673bc435SNathan Fontenot { 557673bc435SNathan Fontenot return sprintf(buf, "%s\n", "memory,cpu"); 558673bc435SNathan Fontenot } 559673bc435SNathan Fontenot 5606f428096SGreg Kroah-Hartman static CLASS_ATTR_RW(dlpar); 561999e2dadSNathan Fontenot 562e2d59152SMichael Ellerman int __init dlpar_workqueue_init(void) 5631a8061c4SNathan Fontenot { 564e2d59152SMichael Ellerman if (pseries_hp_wq) 565e2d59152SMichael Ellerman return 0; 566e2d59152SMichael Ellerman 5679054619eSJohn Allen pseries_hp_wq = alloc_workqueue("pseries hotplug workqueue", 5689054619eSJohn Allen WQ_UNBOUND, 1); 569e2d59152SMichael Ellerman 570e2d59152SMichael Ellerman return pseries_hp_wq ? 0 : -ENOMEM; 571e2d59152SMichael Ellerman } 572e2d59152SMichael Ellerman 573e2d59152SMichael Ellerman static int __init dlpar_sysfs_init(void) 574e2d59152SMichael Ellerman { 575e2d59152SMichael Ellerman int rc; 576e2d59152SMichael Ellerman 577e2d59152SMichael Ellerman rc = dlpar_workqueue_init(); 578e2d59152SMichael Ellerman if (rc) 579e2d59152SMichael Ellerman return rc; 580e2d59152SMichael Ellerman 581183deeeaSNathan Fontenot return sysfs_create_file(kernel_kobj, &class_attr_dlpar.attr); 5821a8061c4SNathan Fontenot } 583e2d59152SMichael Ellerman machine_device_initcall(pseries, dlpar_sysfs_init); 5841a8061c4SNathan Fontenot 585