1ab519a01SNathan Fontenot /* 2ab519a01SNathan Fontenot * Support for dynamic reconfiguration for PCI, Memory, and CPU 3ab519a01SNathan Fontenot * Hotplug and Dynamic Logical Partitioning on RPA platforms. 4ab519a01SNathan Fontenot * 5ab519a01SNathan Fontenot * Copyright (C) 2009 Nathan Fontenot 6ab519a01SNathan Fontenot * Copyright (C) 2009 IBM Corporation 7ab519a01SNathan Fontenot * 8ab519a01SNathan Fontenot * This program is free software; you can redistribute it and/or 9ab519a01SNathan Fontenot * modify it under the terms of the GNU General Public License version 10ab519a01SNathan Fontenot * 2 as published by the Free Software Foundation. 11ab519a01SNathan Fontenot */ 12ab519a01SNathan Fontenot 13999e2dadSNathan Fontenot #define pr_fmt(fmt) "dlpar: " fmt 14999e2dadSNathan Fontenot 15ab519a01SNathan Fontenot #include <linux/kernel.h> 16ab519a01SNathan Fontenot #include <linux/notifier.h> 17ab519a01SNathan Fontenot #include <linux/spinlock.h> 18ab519a01SNathan Fontenot #include <linux/cpu.h> 195a0e3ad6STejun Heo #include <linux/slab.h> 201cf3d8b3SNathan Fontenot #include <linux/of.h> 2106bacefcSAndy Shevchenko 2206bacefcSAndy Shevchenko #include "of_helpers.h" 231217d34bSAnton Blanchard #include "pseries.h" 24ab519a01SNathan Fontenot 25ab519a01SNathan Fontenot #include <asm/prom.h> 26ab519a01SNathan Fontenot #include <asm/machdep.h> 277c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 28ab519a01SNathan Fontenot #include <asm/rtas.h> 29ab519a01SNathan Fontenot 307c98bd72SDaniel Axtens static struct workqueue_struct *pseries_hp_wq; 319054619eSJohn Allen 329054619eSJohn Allen struct pseries_hp_work { 339054619eSJohn Allen struct work_struct work; 349054619eSJohn Allen struct pseries_hp_errorlog *errlog; 359054619eSJohn Allen struct completion *hp_completion; 369054619eSJohn Allen int *rc; 379054619eSJohn Allen }; 389054619eSJohn Allen 39ab519a01SNathan Fontenot struct cc_workarea { 40d6f1e7abSBharata B Rao __be32 drc_index; 41d6f1e7abSBharata B Rao __be32 zero; 42d6f1e7abSBharata B Rao __be32 name_offset; 43d6f1e7abSBharata B Rao __be32 prop_length; 44d6f1e7abSBharata B Rao __be32 prop_offset; 45ab519a01SNathan Fontenot }; 46ab519a01SNathan Fontenot 4720648974SNathan Fontenot void dlpar_free_cc_property(struct property *prop) 48ab519a01SNathan Fontenot { 49ab519a01SNathan Fontenot kfree(prop->name); 50ab519a01SNathan Fontenot kfree(prop->value); 51ab519a01SNathan Fontenot kfree(prop); 52ab519a01SNathan Fontenot } 53ab519a01SNathan Fontenot 54ab519a01SNathan Fontenot static struct property *dlpar_parse_cc_property(struct cc_workarea *ccwa) 55ab519a01SNathan Fontenot { 56ab519a01SNathan Fontenot struct property *prop; 57ab519a01SNathan Fontenot char *name; 58ab519a01SNathan Fontenot char *value; 59ab519a01SNathan Fontenot 60ab519a01SNathan Fontenot prop = kzalloc(sizeof(*prop), GFP_KERNEL); 61ab519a01SNathan Fontenot if (!prop) 62ab519a01SNathan Fontenot return NULL; 63ab519a01SNathan Fontenot 64d6f1e7abSBharata B Rao name = (char *)ccwa + be32_to_cpu(ccwa->name_offset); 65ab519a01SNathan Fontenot prop->name = kstrdup(name, GFP_KERNEL); 66ab519a01SNathan Fontenot 67d6f1e7abSBharata B Rao prop->length = be32_to_cpu(ccwa->prop_length); 68d6f1e7abSBharata B Rao value = (char *)ccwa + be32_to_cpu(ccwa->prop_offset); 69e72ed6b5SNishanth Aravamudan prop->value = kmemdup(value, prop->length, GFP_KERNEL); 70ab519a01SNathan Fontenot if (!prop->value) { 71ab519a01SNathan Fontenot dlpar_free_cc_property(prop); 72ab519a01SNathan Fontenot return NULL; 73ab519a01SNathan Fontenot } 74ab519a01SNathan Fontenot 75ab519a01SNathan Fontenot return prop; 76ab519a01SNathan Fontenot } 77ab519a01SNathan Fontenot 788d5ff320STyrel Datwyler static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa, 798d5ff320STyrel Datwyler const char *path) 80ab519a01SNathan Fontenot { 81ab519a01SNathan Fontenot struct device_node *dn; 82ab519a01SNathan Fontenot char *name; 83ab519a01SNathan Fontenot 848d5ff320STyrel Datwyler /* If parent node path is "/" advance path to NULL terminator to 858d5ff320STyrel Datwyler * prevent double leading slashs in full_name. 868d5ff320STyrel Datwyler */ 878d5ff320STyrel Datwyler if (!path[1]) 888d5ff320STyrel Datwyler path++; 898d5ff320STyrel Datwyler 90ab519a01SNathan Fontenot dn = kzalloc(sizeof(*dn), GFP_KERNEL); 91ab519a01SNathan Fontenot if (!dn) 92ab519a01SNathan Fontenot return NULL; 93ab519a01SNathan Fontenot 94d6f1e7abSBharata B Rao name = (char *)ccwa + be32_to_cpu(ccwa->name_offset); 958d5ff320STyrel Datwyler dn->full_name = kasprintf(GFP_KERNEL, "%s/%s", path, name); 96ab519a01SNathan Fontenot if (!dn->full_name) { 97ab519a01SNathan Fontenot kfree(dn); 98ab519a01SNathan Fontenot return NULL; 99ab519a01SNathan Fontenot } 100ab519a01SNathan Fontenot 1011578cb76STyrel Datwyler of_node_set_flag(dn, OF_DYNAMIC); 10297a9a717STyrel Datwyler of_node_init(dn); 1031578cb76STyrel Datwyler 104ab519a01SNathan Fontenot return dn; 105ab519a01SNathan Fontenot } 106ab519a01SNathan Fontenot 107ab519a01SNathan Fontenot static void dlpar_free_one_cc_node(struct device_node *dn) 108ab519a01SNathan Fontenot { 109ab519a01SNathan Fontenot struct property *prop; 110ab519a01SNathan Fontenot 111ab519a01SNathan Fontenot while (dn->properties) { 112ab519a01SNathan Fontenot prop = dn->properties; 113ab519a01SNathan Fontenot dn->properties = prop->next; 114ab519a01SNathan Fontenot dlpar_free_cc_property(prop); 115ab519a01SNathan Fontenot } 116ab519a01SNathan Fontenot 117ab519a01SNathan Fontenot kfree(dn->full_name); 118ab519a01SNathan Fontenot kfree(dn); 119ab519a01SNathan Fontenot } 120ab519a01SNathan Fontenot 12120648974SNathan Fontenot void dlpar_free_cc_nodes(struct device_node *dn) 122ab519a01SNathan Fontenot { 123ab519a01SNathan Fontenot if (dn->child) 124ab519a01SNathan Fontenot dlpar_free_cc_nodes(dn->child); 125ab519a01SNathan Fontenot 126ab519a01SNathan Fontenot if (dn->sibling) 127ab519a01SNathan Fontenot dlpar_free_cc_nodes(dn->sibling); 128ab519a01SNathan Fontenot 129ab519a01SNathan Fontenot dlpar_free_one_cc_node(dn); 130ab519a01SNathan Fontenot } 131ab519a01SNathan Fontenot 1329c740025SAnton Blanchard #define COMPLETE 0 133ab519a01SNathan Fontenot #define NEXT_SIBLING 1 134ab519a01SNathan Fontenot #define NEXT_CHILD 2 135ab519a01SNathan Fontenot #define NEXT_PROPERTY 3 136ab519a01SNathan Fontenot #define PREV_PARENT 4 137ab519a01SNathan Fontenot #define MORE_MEMORY 5 138ab519a01SNathan Fontenot #define CALL_AGAIN -2 139ab519a01SNathan Fontenot #define ERR_CFG_USE -9003 140ab519a01SNathan Fontenot 141d6f1e7abSBharata B Rao struct device_node *dlpar_configure_connector(__be32 drc_index, 1428d5ff320STyrel Datwyler struct device_node *parent) 143ab519a01SNathan Fontenot { 144ab519a01SNathan Fontenot struct device_node *dn; 145ab519a01SNathan Fontenot struct device_node *first_dn = NULL; 146ab519a01SNathan Fontenot struct device_node *last_dn = NULL; 147ab519a01SNathan Fontenot struct property *property; 148ab519a01SNathan Fontenot struct property *last_property = NULL; 149ab519a01SNathan Fontenot struct cc_workarea *ccwa; 15093f68f1eSNathan Fontenot char *data_buf; 1518d5ff320STyrel Datwyler const char *parent_path = parent->full_name; 152ab519a01SNathan Fontenot int cc_token; 15393f68f1eSNathan Fontenot int rc = -1; 154ab519a01SNathan Fontenot 155ab519a01SNathan Fontenot cc_token = rtas_token("ibm,configure-connector"); 156ab519a01SNathan Fontenot if (cc_token == RTAS_UNKNOWN_SERVICE) 157ab519a01SNathan Fontenot return NULL; 158ab519a01SNathan Fontenot 15993f68f1eSNathan Fontenot data_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL); 16093f68f1eSNathan Fontenot if (!data_buf) 16193f68f1eSNathan Fontenot return NULL; 16293f68f1eSNathan Fontenot 16393f68f1eSNathan Fontenot ccwa = (struct cc_workarea *)&data_buf[0]; 164ab519a01SNathan Fontenot ccwa->drc_index = drc_index; 165ab519a01SNathan Fontenot ccwa->zero = 0; 166ab519a01SNathan Fontenot 16793f68f1eSNathan Fontenot do { 16893f68f1eSNathan Fontenot /* Since we release the rtas_data_buf lock between configure 16993f68f1eSNathan Fontenot * connector calls we want to re-populate the rtas_data_buffer 17093f68f1eSNathan Fontenot * with the contents of the previous call. 17193f68f1eSNathan Fontenot */ 17293f68f1eSNathan Fontenot spin_lock(&rtas_data_buf_lock); 17393f68f1eSNathan Fontenot 17493f68f1eSNathan Fontenot memcpy(rtas_data_buf, data_buf, RTAS_DATA_BUF_SIZE); 175ab519a01SNathan Fontenot rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL); 17693f68f1eSNathan Fontenot memcpy(data_buf, rtas_data_buf, RTAS_DATA_BUF_SIZE); 17793f68f1eSNathan Fontenot 17893f68f1eSNathan Fontenot spin_unlock(&rtas_data_buf_lock); 17993f68f1eSNathan Fontenot 180ab519a01SNathan Fontenot switch (rc) { 1819c740025SAnton Blanchard case COMPLETE: 1829c740025SAnton Blanchard break; 1839c740025SAnton Blanchard 184ab519a01SNathan Fontenot case NEXT_SIBLING: 1858d5ff320STyrel Datwyler dn = dlpar_parse_cc_node(ccwa, parent_path); 186ab519a01SNathan Fontenot if (!dn) 187ab519a01SNathan Fontenot goto cc_error; 188ab519a01SNathan Fontenot 189ab519a01SNathan Fontenot dn->parent = last_dn->parent; 190ab519a01SNathan Fontenot last_dn->sibling = dn; 191ab519a01SNathan Fontenot last_dn = dn; 192ab519a01SNathan Fontenot break; 193ab519a01SNathan Fontenot 194ab519a01SNathan Fontenot case NEXT_CHILD: 1958d5ff320STyrel Datwyler if (first_dn) 1968d5ff320STyrel Datwyler parent_path = last_dn->full_name; 1978d5ff320STyrel Datwyler 1988d5ff320STyrel Datwyler dn = dlpar_parse_cc_node(ccwa, parent_path); 199ab519a01SNathan Fontenot if (!dn) 200ab519a01SNathan Fontenot goto cc_error; 201ab519a01SNathan Fontenot 2028d5ff320STyrel Datwyler if (!first_dn) { 2038d5ff320STyrel Datwyler dn->parent = parent; 204ab519a01SNathan Fontenot first_dn = dn; 2058d5ff320STyrel Datwyler } else { 206ab519a01SNathan Fontenot dn->parent = last_dn; 207ab519a01SNathan Fontenot if (last_dn) 208ab519a01SNathan Fontenot last_dn->child = dn; 209ab519a01SNathan Fontenot } 210ab519a01SNathan Fontenot 211ab519a01SNathan Fontenot last_dn = dn; 212ab519a01SNathan Fontenot break; 213ab519a01SNathan Fontenot 214ab519a01SNathan Fontenot case NEXT_PROPERTY: 215ab519a01SNathan Fontenot property = dlpar_parse_cc_property(ccwa); 216ab519a01SNathan Fontenot if (!property) 217ab519a01SNathan Fontenot goto cc_error; 218ab519a01SNathan Fontenot 219ab519a01SNathan Fontenot if (!last_dn->properties) 220ab519a01SNathan Fontenot last_dn->properties = property; 221ab519a01SNathan Fontenot else 222ab519a01SNathan Fontenot last_property->next = property; 223ab519a01SNathan Fontenot 224ab519a01SNathan Fontenot last_property = property; 225ab519a01SNathan Fontenot break; 226ab519a01SNathan Fontenot 227ab519a01SNathan Fontenot case PREV_PARENT: 228ab519a01SNathan Fontenot last_dn = last_dn->parent; 2298d5ff320STyrel Datwyler parent_path = last_dn->parent->full_name; 230ab519a01SNathan Fontenot break; 231ab519a01SNathan Fontenot 232ab519a01SNathan Fontenot case CALL_AGAIN: 233ab519a01SNathan Fontenot break; 234ab519a01SNathan Fontenot 235ab519a01SNathan Fontenot case MORE_MEMORY: 236ab519a01SNathan Fontenot case ERR_CFG_USE: 237ab519a01SNathan Fontenot default: 238ab519a01SNathan Fontenot printk(KERN_ERR "Unexpected Error (%d) " 239ab519a01SNathan Fontenot "returned from configure-connector\n", rc); 240ab519a01SNathan Fontenot goto cc_error; 241ab519a01SNathan Fontenot } 24293f68f1eSNathan Fontenot } while (rc); 243ab519a01SNathan Fontenot 244ab519a01SNathan Fontenot cc_error: 24593f68f1eSNathan Fontenot kfree(data_buf); 24693f68f1eSNathan Fontenot 24793f68f1eSNathan Fontenot if (rc) { 248ab519a01SNathan Fontenot if (first_dn) 249ab519a01SNathan Fontenot dlpar_free_cc_nodes(first_dn); 25093f68f1eSNathan Fontenot 251ab519a01SNathan Fontenot return NULL; 252ab519a01SNathan Fontenot } 253ab519a01SNathan Fontenot 25493f68f1eSNathan Fontenot return first_dn; 25593f68f1eSNathan Fontenot } 25693f68f1eSNathan Fontenot 257ab519a01SNathan Fontenot int dlpar_attach_node(struct device_node *dn) 258ab519a01SNathan Fontenot { 259ab519a01SNathan Fontenot int rc; 260ab519a01SNathan Fontenot 26106bacefcSAndy Shevchenko dn->parent = pseries_of_derive_parent(dn->full_name); 26206bacefcSAndy Shevchenko if (IS_ERR(dn->parent)) 26306bacefcSAndy Shevchenko return PTR_ERR(dn->parent); 264ab519a01SNathan Fontenot 2651cf3d8b3SNathan Fontenot rc = of_attach_node(dn); 2663aef19f0SAkinobu Mita if (rc) { 267*b7c670d6SRob Herring printk(KERN_ERR "Failed to add device node %pOF\n", dn); 2683aef19f0SAkinobu Mita return rc; 269ab519a01SNathan Fontenot } 270ab519a01SNathan Fontenot 271ab519a01SNathan Fontenot of_node_put(dn->parent); 272ab519a01SNathan Fontenot return 0; 273ab519a01SNathan Fontenot } 274ab519a01SNathan Fontenot 275ab519a01SNathan Fontenot int dlpar_detach_node(struct device_node *dn) 276ab519a01SNathan Fontenot { 2775935ff43STyrel Datwyler struct device_node *child; 2781cf3d8b3SNathan Fontenot int rc; 279ab519a01SNathan Fontenot 2805935ff43STyrel Datwyler child = of_get_next_child(dn, NULL); 2815935ff43STyrel Datwyler while (child) { 2825935ff43STyrel Datwyler dlpar_detach_node(child); 2835935ff43STyrel Datwyler child = of_get_next_child(dn, child); 2845935ff43STyrel Datwyler } 2855935ff43STyrel Datwyler 2861cf3d8b3SNathan Fontenot rc = of_detach_node(dn); 2871cf3d8b3SNathan Fontenot if (rc) 2881cf3d8b3SNathan Fontenot return rc; 2891cf3d8b3SNathan Fontenot 290ab519a01SNathan Fontenot return 0; 291ab519a01SNathan Fontenot } 292ab519a01SNathan Fontenot 293ab519a01SNathan Fontenot #define DR_ENTITY_SENSE 9003 294ab519a01SNathan Fontenot #define DR_ENTITY_PRESENT 1 295ab519a01SNathan Fontenot #define DR_ENTITY_UNUSABLE 2 296ab519a01SNathan Fontenot #define ALLOCATION_STATE 9003 297ab519a01SNathan Fontenot #define ALLOC_UNUSABLE 0 298ab519a01SNathan Fontenot #define ALLOC_USABLE 1 299ab519a01SNathan Fontenot #define ISOLATION_STATE 9001 300ab519a01SNathan Fontenot #define ISOLATE 0 301ab519a01SNathan Fontenot #define UNISOLATE 1 302ab519a01SNathan Fontenot 303ab519a01SNathan Fontenot int dlpar_acquire_drc(u32 drc_index) 304ab519a01SNathan Fontenot { 305ab519a01SNathan Fontenot int dr_status, rc; 306ab519a01SNathan Fontenot 307ab519a01SNathan Fontenot rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status, 308ab519a01SNathan Fontenot DR_ENTITY_SENSE, drc_index); 309ab519a01SNathan Fontenot if (rc || dr_status != DR_ENTITY_UNUSABLE) 310ab519a01SNathan Fontenot return -1; 311ab519a01SNathan Fontenot 312ab519a01SNathan Fontenot rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE); 313ab519a01SNathan Fontenot if (rc) 314ab519a01SNathan Fontenot return rc; 315ab519a01SNathan Fontenot 316ab519a01SNathan Fontenot rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 317ab519a01SNathan Fontenot if (rc) { 318ab519a01SNathan Fontenot rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 319ab519a01SNathan Fontenot return rc; 320ab519a01SNathan Fontenot } 321ab519a01SNathan Fontenot 322ab519a01SNathan Fontenot return 0; 323ab519a01SNathan Fontenot } 324ab519a01SNathan Fontenot 325ab519a01SNathan Fontenot int dlpar_release_drc(u32 drc_index) 326ab519a01SNathan Fontenot { 327ab519a01SNathan Fontenot int dr_status, rc; 328ab519a01SNathan Fontenot 329ab519a01SNathan Fontenot rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status, 330ab519a01SNathan Fontenot DR_ENTITY_SENSE, drc_index); 331ab519a01SNathan Fontenot if (rc || dr_status != DR_ENTITY_PRESENT) 332ab519a01SNathan Fontenot return -1; 333ab519a01SNathan Fontenot 334ab519a01SNathan Fontenot rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE); 335ab519a01SNathan Fontenot if (rc) 336ab519a01SNathan Fontenot return rc; 337ab519a01SNathan Fontenot 338ab519a01SNathan Fontenot rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 339ab519a01SNathan Fontenot if (rc) { 340ab519a01SNathan Fontenot rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 341ab519a01SNathan Fontenot return rc; 342ab519a01SNathan Fontenot } 343ab519a01SNathan Fontenot 344ab519a01SNathan Fontenot return 0; 345ab519a01SNathan Fontenot } 346ab519a01SNathan Fontenot 347999e2dadSNathan Fontenot static int handle_dlpar_errorlog(struct pseries_hp_errorlog *hp_elog) 348999e2dadSNathan Fontenot { 349999e2dadSNathan Fontenot int rc; 350999e2dadSNathan Fontenot 351999e2dadSNathan Fontenot /* pseries error logs are in BE format, convert to cpu type */ 352999e2dadSNathan Fontenot switch (hp_elog->id_type) { 353999e2dadSNathan Fontenot case PSERIES_HP_ELOG_ID_DRC_COUNT: 354999e2dadSNathan Fontenot hp_elog->_drc_u.drc_count = 355999e2dadSNathan Fontenot be32_to_cpu(hp_elog->_drc_u.drc_count); 356999e2dadSNathan Fontenot break; 357999e2dadSNathan Fontenot case PSERIES_HP_ELOG_ID_DRC_INDEX: 358999e2dadSNathan Fontenot hp_elog->_drc_u.drc_index = 359999e2dadSNathan Fontenot be32_to_cpu(hp_elog->_drc_u.drc_index); 360333f7b76SSahil Mehta break; 361333f7b76SSahil Mehta case PSERIES_HP_ELOG_ID_DRC_IC: 362333f7b76SSahil Mehta hp_elog->_drc_u.ic.count = 363333f7b76SSahil Mehta be32_to_cpu(hp_elog->_drc_u.ic.count); 364333f7b76SSahil Mehta hp_elog->_drc_u.ic.index = 365333f7b76SSahil Mehta be32_to_cpu(hp_elog->_drc_u.ic.index); 366999e2dadSNathan Fontenot } 367999e2dadSNathan Fontenot 368999e2dadSNathan Fontenot switch (hp_elog->resource) { 369999e2dadSNathan Fontenot case PSERIES_HP_ELOG_RESOURCE_MEM: 370999e2dadSNathan Fontenot rc = dlpar_memory(hp_elog); 371999e2dadSNathan Fontenot break; 372e9d764f8SNathan Fontenot case PSERIES_HP_ELOG_RESOURCE_CPU: 373e9d764f8SNathan Fontenot rc = dlpar_cpu(hp_elog); 374e9d764f8SNathan Fontenot break; 375999e2dadSNathan Fontenot default: 376999e2dadSNathan Fontenot pr_warn_ratelimited("Invalid resource (%d) specified\n", 377999e2dadSNathan Fontenot hp_elog->resource); 378999e2dadSNathan Fontenot rc = -EINVAL; 379999e2dadSNathan Fontenot } 380999e2dadSNathan Fontenot 381999e2dadSNathan Fontenot return rc; 382999e2dadSNathan Fontenot } 383999e2dadSNathan Fontenot 3847c98bd72SDaniel Axtens static void pseries_hp_work_fn(struct work_struct *work) 3859054619eSJohn Allen { 3869054619eSJohn Allen struct pseries_hp_work *hp_work = 3879054619eSJohn Allen container_of(work, struct pseries_hp_work, work); 3889054619eSJohn Allen 3899054619eSJohn Allen if (hp_work->rc) 3909054619eSJohn Allen *(hp_work->rc) = handle_dlpar_errorlog(hp_work->errlog); 3919054619eSJohn Allen else 3929054619eSJohn Allen handle_dlpar_errorlog(hp_work->errlog); 3939054619eSJohn Allen 3949054619eSJohn Allen if (hp_work->hp_completion) 3959054619eSJohn Allen complete(hp_work->hp_completion); 3969054619eSJohn Allen 3979054619eSJohn Allen kfree(hp_work->errlog); 3989054619eSJohn Allen kfree((void *)work); 3999054619eSJohn Allen } 4009054619eSJohn Allen 4019054619eSJohn Allen void queue_hotplug_event(struct pseries_hp_errorlog *hp_errlog, 4029054619eSJohn Allen struct completion *hotplug_done, int *rc) 4039054619eSJohn Allen { 4049054619eSJohn Allen struct pseries_hp_work *work; 4059054619eSJohn Allen struct pseries_hp_errorlog *hp_errlog_copy; 4069054619eSJohn Allen 4079054619eSJohn Allen hp_errlog_copy = kmalloc(sizeof(struct pseries_hp_errorlog), 4089054619eSJohn Allen GFP_KERNEL); 4099054619eSJohn Allen memcpy(hp_errlog_copy, hp_errlog, sizeof(struct pseries_hp_errorlog)); 4109054619eSJohn Allen 4119054619eSJohn Allen work = kmalloc(sizeof(struct pseries_hp_work), GFP_KERNEL); 4129054619eSJohn Allen if (work) { 4139054619eSJohn Allen INIT_WORK((struct work_struct *)work, pseries_hp_work_fn); 4149054619eSJohn Allen work->errlog = hp_errlog_copy; 4159054619eSJohn Allen work->hp_completion = hotplug_done; 4169054619eSJohn Allen work->rc = rc; 4179054619eSJohn Allen queue_work(pseries_hp_wq, (struct work_struct *)work); 4189054619eSJohn Allen } else { 4199054619eSJohn Allen *rc = -ENOMEM; 42090ce3514SAndrew Donnellan kfree(hp_errlog_copy); 4219054619eSJohn Allen complete(hotplug_done); 4229054619eSJohn Allen } 4239054619eSJohn Allen } 4249054619eSJohn Allen 42525b587fbSNathan Fontenot static int dlpar_parse_resource(char **cmd, struct pseries_hp_errorlog *hp_elog) 42625b587fbSNathan Fontenot { 42725b587fbSNathan Fontenot char *arg; 42825b587fbSNathan Fontenot 42925b587fbSNathan Fontenot arg = strsep(cmd, " "); 43025b587fbSNathan Fontenot if (!arg) 43125b587fbSNathan Fontenot return -EINVAL; 43225b587fbSNathan Fontenot 43325b587fbSNathan Fontenot if (sysfs_streq(arg, "memory")) { 43425b587fbSNathan Fontenot hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_MEM; 43525b587fbSNathan Fontenot } else if (sysfs_streq(arg, "cpu")) { 43625b587fbSNathan Fontenot hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_CPU; 43725b587fbSNathan Fontenot } else { 43825b587fbSNathan Fontenot pr_err("Invalid resource specified.\n"); 43925b587fbSNathan Fontenot return -EINVAL; 44025b587fbSNathan Fontenot } 44125b587fbSNathan Fontenot 44225b587fbSNathan Fontenot return 0; 44325b587fbSNathan Fontenot } 44425b587fbSNathan Fontenot 44525b587fbSNathan Fontenot static int dlpar_parse_action(char **cmd, struct pseries_hp_errorlog *hp_elog) 44625b587fbSNathan Fontenot { 44725b587fbSNathan Fontenot char *arg; 44825b587fbSNathan Fontenot 44925b587fbSNathan Fontenot arg = strsep(cmd, " "); 45025b587fbSNathan Fontenot if (!arg) 45125b587fbSNathan Fontenot return -EINVAL; 45225b587fbSNathan Fontenot 45325b587fbSNathan Fontenot if (sysfs_streq(arg, "add")) { 45425b587fbSNathan Fontenot hp_elog->action = PSERIES_HP_ELOG_ACTION_ADD; 45525b587fbSNathan Fontenot } else if (sysfs_streq(arg, "remove")) { 45625b587fbSNathan Fontenot hp_elog->action = PSERIES_HP_ELOG_ACTION_REMOVE; 45725b587fbSNathan Fontenot } else { 45825b587fbSNathan Fontenot pr_err("Invalid action specified.\n"); 45925b587fbSNathan Fontenot return -EINVAL; 46025b587fbSNathan Fontenot } 46125b587fbSNathan Fontenot 46225b587fbSNathan Fontenot return 0; 46325b587fbSNathan Fontenot } 46425b587fbSNathan Fontenot 46525b587fbSNathan Fontenot static int dlpar_parse_id_type(char **cmd, struct pseries_hp_errorlog *hp_elog) 46625b587fbSNathan Fontenot { 46725b587fbSNathan Fontenot char *arg; 46825b587fbSNathan Fontenot u32 count, index; 46925b587fbSNathan Fontenot 47025b587fbSNathan Fontenot arg = strsep(cmd, " "); 47125b587fbSNathan Fontenot if (!arg) 47225b587fbSNathan Fontenot return -EINVAL; 47325b587fbSNathan Fontenot 474333f7b76SSahil Mehta if (sysfs_streq(arg, "indexed-count")) { 475333f7b76SSahil Mehta hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_IC; 476333f7b76SSahil Mehta arg = strsep(cmd, " "); 477333f7b76SSahil Mehta if (!arg) { 478333f7b76SSahil Mehta pr_err("No DRC count specified.\n"); 479333f7b76SSahil Mehta return -EINVAL; 480333f7b76SSahil Mehta } 481333f7b76SSahil Mehta 482333f7b76SSahil Mehta if (kstrtou32(arg, 0, &count)) { 483333f7b76SSahil Mehta pr_err("Invalid DRC count specified.\n"); 484333f7b76SSahil Mehta return -EINVAL; 485333f7b76SSahil Mehta } 486333f7b76SSahil Mehta 487333f7b76SSahil Mehta arg = strsep(cmd, " "); 488333f7b76SSahil Mehta if (!arg) { 489333f7b76SSahil Mehta pr_err("No DRC Index specified.\n"); 490333f7b76SSahil Mehta return -EINVAL; 491333f7b76SSahil Mehta } 492333f7b76SSahil Mehta 493333f7b76SSahil Mehta if (kstrtou32(arg, 0, &index)) { 494333f7b76SSahil Mehta pr_err("Invalid DRC Index specified.\n"); 495333f7b76SSahil Mehta return -EINVAL; 496333f7b76SSahil Mehta } 497333f7b76SSahil Mehta 498333f7b76SSahil Mehta hp_elog->_drc_u.ic.count = cpu_to_be32(count); 499333f7b76SSahil Mehta hp_elog->_drc_u.ic.index = cpu_to_be32(index); 500333f7b76SSahil Mehta } else if (sysfs_streq(arg, "index")) { 50125b587fbSNathan Fontenot hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_INDEX; 50225b587fbSNathan Fontenot arg = strsep(cmd, " "); 50325b587fbSNathan Fontenot if (!arg) { 50425b587fbSNathan Fontenot pr_err("No DRC Index specified.\n"); 50525b587fbSNathan Fontenot return -EINVAL; 50625b587fbSNathan Fontenot } 50725b587fbSNathan Fontenot 50825b587fbSNathan Fontenot if (kstrtou32(arg, 0, &index)) { 50925b587fbSNathan Fontenot pr_err("Invalid DRC Index specified.\n"); 51025b587fbSNathan Fontenot return -EINVAL; 51125b587fbSNathan Fontenot } 51225b587fbSNathan Fontenot 51325b587fbSNathan Fontenot hp_elog->_drc_u.drc_index = cpu_to_be32(index); 51425b587fbSNathan Fontenot } else if (sysfs_streq(arg, "count")) { 51525b587fbSNathan Fontenot hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_COUNT; 51625b587fbSNathan Fontenot arg = strsep(cmd, " "); 51725b587fbSNathan Fontenot if (!arg) { 51825b587fbSNathan Fontenot pr_err("No DRC count specified.\n"); 51925b587fbSNathan Fontenot return -EINVAL; 52025b587fbSNathan Fontenot } 52125b587fbSNathan Fontenot 52225b587fbSNathan Fontenot if (kstrtou32(arg, 0, &count)) { 52325b587fbSNathan Fontenot pr_err("Invalid DRC count specified.\n"); 52425b587fbSNathan Fontenot return -EINVAL; 52525b587fbSNathan Fontenot } 52625b587fbSNathan Fontenot 52725b587fbSNathan Fontenot hp_elog->_drc_u.drc_count = cpu_to_be32(count); 52825b587fbSNathan Fontenot } else { 52925b587fbSNathan Fontenot pr_err("Invalid id_type specified.\n"); 53025b587fbSNathan Fontenot return -EINVAL; 53125b587fbSNathan Fontenot } 53225b587fbSNathan Fontenot 53325b587fbSNathan Fontenot return 0; 53425b587fbSNathan Fontenot } 53525b587fbSNathan Fontenot 536999e2dadSNathan Fontenot static ssize_t dlpar_store(struct class *class, struct class_attribute *attr, 537999e2dadSNathan Fontenot const char *buf, size_t count) 538999e2dadSNathan Fontenot { 539999e2dadSNathan Fontenot struct pseries_hp_errorlog *hp_elog; 5409054619eSJohn Allen struct completion hotplug_done; 54125b587fbSNathan Fontenot char *argbuf; 54225b587fbSNathan Fontenot char *args; 543999e2dadSNathan Fontenot int rc; 544999e2dadSNathan Fontenot 54525b587fbSNathan Fontenot args = argbuf = kstrdup(buf, GFP_KERNEL); 546999e2dadSNathan Fontenot hp_elog = kzalloc(sizeof(*hp_elog), GFP_KERNEL); 54725b587fbSNathan Fontenot if (!hp_elog || !argbuf) { 54825b587fbSNathan Fontenot pr_info("Could not allocate resources for DLPAR operation\n"); 54925b587fbSNathan Fontenot kfree(argbuf); 55025b587fbSNathan Fontenot kfree(hp_elog); 55125b587fbSNathan Fontenot return -ENOMEM; 552999e2dadSNathan Fontenot } 553999e2dadSNathan Fontenot 55425b587fbSNathan Fontenot /* 55525b587fbSNathan Fontenot * Parse out the request from the user, this will be in the form: 556999e2dadSNathan Fontenot * <resource> <action> <id_type> <id> 557999e2dadSNathan Fontenot */ 55825b587fbSNathan Fontenot rc = dlpar_parse_resource(&args, hp_elog); 55925b587fbSNathan Fontenot if (rc) 560999e2dadSNathan Fontenot goto dlpar_store_out; 561999e2dadSNathan Fontenot 56225b587fbSNathan Fontenot rc = dlpar_parse_action(&args, hp_elog); 56325b587fbSNathan Fontenot if (rc) 564999e2dadSNathan Fontenot goto dlpar_store_out; 565999e2dadSNathan Fontenot 56625b587fbSNathan Fontenot rc = dlpar_parse_id_type(&args, hp_elog); 56725b587fbSNathan Fontenot if (rc) 568999e2dadSNathan Fontenot goto dlpar_store_out; 569999e2dadSNathan Fontenot 5701dc75956SJohn Allen init_completion(&hotplug_done); 5711dc75956SJohn Allen queue_hotplug_event(hp_elog, &hotplug_done, &rc); 5721dc75956SJohn Allen wait_for_completion(&hotplug_done); 573999e2dadSNathan Fontenot 574999e2dadSNathan Fontenot dlpar_store_out: 57525b587fbSNathan Fontenot kfree(argbuf); 576999e2dadSNathan Fontenot kfree(hp_elog); 57725b587fbSNathan Fontenot 57825b587fbSNathan Fontenot if (rc) 57925b587fbSNathan Fontenot pr_err("Could not handle DLPAR request \"%s\"\n", buf); 58025b587fbSNathan Fontenot 581999e2dadSNathan Fontenot return rc ? rc : count; 582999e2dadSNathan Fontenot } 583999e2dadSNathan Fontenot 584673bc435SNathan Fontenot static ssize_t dlpar_show(struct class *class, struct class_attribute *attr, 585673bc435SNathan Fontenot char *buf) 586673bc435SNathan Fontenot { 587673bc435SNathan Fontenot return sprintf(buf, "%s\n", "memory,cpu"); 588673bc435SNathan Fontenot } 589673bc435SNathan Fontenot 5906f428096SGreg Kroah-Hartman static CLASS_ATTR_RW(dlpar); 591999e2dadSNathan Fontenot 5921a8061c4SNathan Fontenot static int __init pseries_dlpar_init(void) 5931a8061c4SNathan Fontenot { 5949054619eSJohn Allen pseries_hp_wq = alloc_workqueue("pseries hotplug workqueue", 5959054619eSJohn Allen WQ_UNBOUND, 1); 596183deeeaSNathan Fontenot return sysfs_create_file(kernel_kobj, &class_attr_dlpar.attr); 5971a8061c4SNathan Fontenot } 5981a8061c4SNathan Fontenot machine_device_initcall(pseries, pseries_dlpar_init); 5991a8061c4SNathan Fontenot 600