162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Support for dynamic reconfiguration for PCI, Memory, and CPU 462306a36Sopenharmony_ci * Hotplug and Dynamic Logical Partitioning on RPA platforms. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2009 Nathan Fontenot 762306a36Sopenharmony_ci * Copyright (C) 2009 IBM Corporation 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define pr_fmt(fmt) "dlpar: " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/notifier.h> 1462306a36Sopenharmony_ci#include <linux/spinlock.h> 1562306a36Sopenharmony_ci#include <linux/cpu.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/of.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "of_helpers.h" 2062306a36Sopenharmony_ci#include "pseries.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <asm/machdep.h> 2362306a36Sopenharmony_ci#include <linux/uaccess.h> 2462306a36Sopenharmony_ci#include <asm/rtas.h> 2562306a36Sopenharmony_ci#include <asm/rtas-work-area.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic struct workqueue_struct *pseries_hp_wq; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistruct pseries_hp_work { 3062306a36Sopenharmony_ci struct work_struct work; 3162306a36Sopenharmony_ci struct pseries_hp_errorlog *errlog; 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistruct cc_workarea { 3562306a36Sopenharmony_ci __be32 drc_index; 3662306a36Sopenharmony_ci __be32 zero; 3762306a36Sopenharmony_ci __be32 name_offset; 3862306a36Sopenharmony_ci __be32 prop_length; 3962306a36Sopenharmony_ci __be32 prop_offset; 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_civoid dlpar_free_cc_property(struct property *prop) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci kfree(prop->name); 4562306a36Sopenharmony_ci kfree(prop->value); 4662306a36Sopenharmony_ci kfree(prop); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic struct property *dlpar_parse_cc_property(struct cc_workarea *ccwa) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct property *prop; 5262306a36Sopenharmony_ci char *name; 5362306a36Sopenharmony_ci char *value; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci prop = kzalloc(sizeof(*prop), GFP_KERNEL); 5662306a36Sopenharmony_ci if (!prop) 5762306a36Sopenharmony_ci return NULL; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci name = (char *)ccwa + be32_to_cpu(ccwa->name_offset); 6062306a36Sopenharmony_ci prop->name = kstrdup(name, GFP_KERNEL); 6162306a36Sopenharmony_ci if (!prop->name) { 6262306a36Sopenharmony_ci dlpar_free_cc_property(prop); 6362306a36Sopenharmony_ci return NULL; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci prop->length = be32_to_cpu(ccwa->prop_length); 6762306a36Sopenharmony_ci value = (char *)ccwa + be32_to_cpu(ccwa->prop_offset); 6862306a36Sopenharmony_ci prop->value = kmemdup(value, prop->length, GFP_KERNEL); 6962306a36Sopenharmony_ci if (!prop->value) { 7062306a36Sopenharmony_ci dlpar_free_cc_property(prop); 7162306a36Sopenharmony_ci return NULL; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return prop; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct device_node *dn; 8062306a36Sopenharmony_ci const char *name; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci dn = kzalloc(sizeof(*dn), GFP_KERNEL); 8362306a36Sopenharmony_ci if (!dn) 8462306a36Sopenharmony_ci return NULL; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci name = (const char *)ccwa + be32_to_cpu(ccwa->name_offset); 8762306a36Sopenharmony_ci dn->full_name = kstrdup(name, GFP_KERNEL); 8862306a36Sopenharmony_ci if (!dn->full_name) { 8962306a36Sopenharmony_ci kfree(dn); 9062306a36Sopenharmony_ci return NULL; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci of_node_set_flag(dn, OF_DYNAMIC); 9462306a36Sopenharmony_ci of_node_init(dn); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return dn; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic void dlpar_free_one_cc_node(struct device_node *dn) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct property *prop; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci while (dn->properties) { 10462306a36Sopenharmony_ci prop = dn->properties; 10562306a36Sopenharmony_ci dn->properties = prop->next; 10662306a36Sopenharmony_ci dlpar_free_cc_property(prop); 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci kfree(dn->full_name); 11062306a36Sopenharmony_ci kfree(dn); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_civoid dlpar_free_cc_nodes(struct device_node *dn) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci if (dn->child) 11662306a36Sopenharmony_ci dlpar_free_cc_nodes(dn->child); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (dn->sibling) 11962306a36Sopenharmony_ci dlpar_free_cc_nodes(dn->sibling); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci dlpar_free_one_cc_node(dn); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci#define COMPLETE 0 12562306a36Sopenharmony_ci#define NEXT_SIBLING 1 12662306a36Sopenharmony_ci#define NEXT_CHILD 2 12762306a36Sopenharmony_ci#define NEXT_PROPERTY 3 12862306a36Sopenharmony_ci#define PREV_PARENT 4 12962306a36Sopenharmony_ci#define MORE_MEMORY 5 13062306a36Sopenharmony_ci#define ERR_CFG_USE -9003 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistruct device_node *dlpar_configure_connector(__be32 drc_index, 13362306a36Sopenharmony_ci struct device_node *parent) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct device_node *dn; 13662306a36Sopenharmony_ci struct device_node *first_dn = NULL; 13762306a36Sopenharmony_ci struct device_node *last_dn = NULL; 13862306a36Sopenharmony_ci struct property *property; 13962306a36Sopenharmony_ci struct property *last_property = NULL; 14062306a36Sopenharmony_ci struct cc_workarea *ccwa; 14162306a36Sopenharmony_ci struct rtas_work_area *work_area; 14262306a36Sopenharmony_ci char *data_buf; 14362306a36Sopenharmony_ci int cc_token; 14462306a36Sopenharmony_ci int rc = -1; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci cc_token = rtas_function_token(RTAS_FN_IBM_CONFIGURE_CONNECTOR); 14762306a36Sopenharmony_ci if (cc_token == RTAS_UNKNOWN_SERVICE) 14862306a36Sopenharmony_ci return NULL; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci work_area = rtas_work_area_alloc(SZ_4K); 15162306a36Sopenharmony_ci data_buf = rtas_work_area_raw_buf(work_area); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci ccwa = (struct cc_workarea *)&data_buf[0]; 15462306a36Sopenharmony_ci ccwa->drc_index = drc_index; 15562306a36Sopenharmony_ci ccwa->zero = 0; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci do { 15862306a36Sopenharmony_ci do { 15962306a36Sopenharmony_ci rc = rtas_call(cc_token, 2, 1, NULL, 16062306a36Sopenharmony_ci rtas_work_area_phys(work_area), NULL); 16162306a36Sopenharmony_ci } while (rtas_busy_delay(rc)); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci switch (rc) { 16462306a36Sopenharmony_ci case COMPLETE: 16562306a36Sopenharmony_ci break; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci case NEXT_SIBLING: 16862306a36Sopenharmony_ci dn = dlpar_parse_cc_node(ccwa); 16962306a36Sopenharmony_ci if (!dn) 17062306a36Sopenharmony_ci goto cc_error; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci dn->parent = last_dn->parent; 17362306a36Sopenharmony_ci last_dn->sibling = dn; 17462306a36Sopenharmony_ci last_dn = dn; 17562306a36Sopenharmony_ci break; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci case NEXT_CHILD: 17862306a36Sopenharmony_ci dn = dlpar_parse_cc_node(ccwa); 17962306a36Sopenharmony_ci if (!dn) 18062306a36Sopenharmony_ci goto cc_error; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (!first_dn) { 18362306a36Sopenharmony_ci dn->parent = parent; 18462306a36Sopenharmony_ci first_dn = dn; 18562306a36Sopenharmony_ci } else { 18662306a36Sopenharmony_ci dn->parent = last_dn; 18762306a36Sopenharmony_ci if (last_dn) 18862306a36Sopenharmony_ci last_dn->child = dn; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci last_dn = dn; 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci case NEXT_PROPERTY: 19562306a36Sopenharmony_ci property = dlpar_parse_cc_property(ccwa); 19662306a36Sopenharmony_ci if (!property) 19762306a36Sopenharmony_ci goto cc_error; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (!last_dn->properties) 20062306a36Sopenharmony_ci last_dn->properties = property; 20162306a36Sopenharmony_ci else 20262306a36Sopenharmony_ci last_property->next = property; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci last_property = property; 20562306a36Sopenharmony_ci break; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci case PREV_PARENT: 20862306a36Sopenharmony_ci last_dn = last_dn->parent; 20962306a36Sopenharmony_ci break; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci case MORE_MEMORY: 21262306a36Sopenharmony_ci case ERR_CFG_USE: 21362306a36Sopenharmony_ci default: 21462306a36Sopenharmony_ci printk(KERN_ERR "Unexpected Error (%d) " 21562306a36Sopenharmony_ci "returned from configure-connector\n", rc); 21662306a36Sopenharmony_ci goto cc_error; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci } while (rc); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cicc_error: 22162306a36Sopenharmony_ci rtas_work_area_free(work_area); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (rc) { 22462306a36Sopenharmony_ci if (first_dn) 22562306a36Sopenharmony_ci dlpar_free_cc_nodes(first_dn); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return NULL; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci return first_dn; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ciint dlpar_attach_node(struct device_node *dn, struct device_node *parent) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci int rc; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci dn->parent = parent; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci rc = of_attach_node(dn); 24062306a36Sopenharmony_ci if (rc) { 24162306a36Sopenharmony_ci printk(KERN_ERR "Failed to add device node %pOF\n", dn); 24262306a36Sopenharmony_ci return rc; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci return 0; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ciint dlpar_detach_node(struct device_node *dn) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct device_node *child; 25162306a36Sopenharmony_ci int rc; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci child = of_get_next_child(dn, NULL); 25462306a36Sopenharmony_ci while (child) { 25562306a36Sopenharmony_ci dlpar_detach_node(child); 25662306a36Sopenharmony_ci child = of_get_next_child(dn, child); 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci rc = of_detach_node(dn); 26062306a36Sopenharmony_ci if (rc) 26162306a36Sopenharmony_ci return rc; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci of_node_put(dn); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return 0; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci#define DR_ENTITY_SENSE 9003 26962306a36Sopenharmony_ci#define DR_ENTITY_PRESENT 1 27062306a36Sopenharmony_ci#define DR_ENTITY_UNUSABLE 2 27162306a36Sopenharmony_ci#define ALLOCATION_STATE 9003 27262306a36Sopenharmony_ci#define ALLOC_UNUSABLE 0 27362306a36Sopenharmony_ci#define ALLOC_USABLE 1 27462306a36Sopenharmony_ci#define ISOLATION_STATE 9001 27562306a36Sopenharmony_ci#define ISOLATE 0 27662306a36Sopenharmony_ci#define UNISOLATE 1 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ciint dlpar_acquire_drc(u32 drc_index) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci int dr_status, rc; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci rc = rtas_get_sensor(DR_ENTITY_SENSE, drc_index, &dr_status); 28362306a36Sopenharmony_ci if (rc || dr_status != DR_ENTITY_UNUSABLE) 28462306a36Sopenharmony_ci return -1; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE); 28762306a36Sopenharmony_ci if (rc) 28862306a36Sopenharmony_ci return rc; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 29162306a36Sopenharmony_ci if (rc) { 29262306a36Sopenharmony_ci rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 29362306a36Sopenharmony_ci return rc; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci return 0; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ciint dlpar_release_drc(u32 drc_index) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci int dr_status, rc; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci rc = rtas_get_sensor(DR_ENTITY_SENSE, drc_index, &dr_status); 30462306a36Sopenharmony_ci if (rc || dr_status != DR_ENTITY_PRESENT) 30562306a36Sopenharmony_ci return -1; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE); 30862306a36Sopenharmony_ci if (rc) 30962306a36Sopenharmony_ci return rc; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 31262306a36Sopenharmony_ci if (rc) { 31362306a36Sopenharmony_ci rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 31462306a36Sopenharmony_ci return rc; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci return 0; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ciint dlpar_unisolate_drc(u32 drc_index) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci int dr_status, rc; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci rc = rtas_get_sensor(DR_ENTITY_SENSE, drc_index, &dr_status); 32562306a36Sopenharmony_ci if (rc || dr_status != DR_ENTITY_PRESENT) 32662306a36Sopenharmony_ci return -1; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci return 0; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ciint handle_dlpar_errorlog(struct pseries_hp_errorlog *hp_elog) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci int rc; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* pseries error logs are in BE format, convert to cpu type */ 33862306a36Sopenharmony_ci switch (hp_elog->id_type) { 33962306a36Sopenharmony_ci case PSERIES_HP_ELOG_ID_DRC_COUNT: 34062306a36Sopenharmony_ci hp_elog->_drc_u.drc_count = 34162306a36Sopenharmony_ci be32_to_cpu(hp_elog->_drc_u.drc_count); 34262306a36Sopenharmony_ci break; 34362306a36Sopenharmony_ci case PSERIES_HP_ELOG_ID_DRC_INDEX: 34462306a36Sopenharmony_ci hp_elog->_drc_u.drc_index = 34562306a36Sopenharmony_ci be32_to_cpu(hp_elog->_drc_u.drc_index); 34662306a36Sopenharmony_ci break; 34762306a36Sopenharmony_ci case PSERIES_HP_ELOG_ID_DRC_IC: 34862306a36Sopenharmony_ci hp_elog->_drc_u.ic.count = 34962306a36Sopenharmony_ci be32_to_cpu(hp_elog->_drc_u.ic.count); 35062306a36Sopenharmony_ci hp_elog->_drc_u.ic.index = 35162306a36Sopenharmony_ci be32_to_cpu(hp_elog->_drc_u.ic.index); 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci switch (hp_elog->resource) { 35562306a36Sopenharmony_ci case PSERIES_HP_ELOG_RESOURCE_MEM: 35662306a36Sopenharmony_ci rc = dlpar_memory(hp_elog); 35762306a36Sopenharmony_ci break; 35862306a36Sopenharmony_ci case PSERIES_HP_ELOG_RESOURCE_CPU: 35962306a36Sopenharmony_ci rc = dlpar_cpu(hp_elog); 36062306a36Sopenharmony_ci break; 36162306a36Sopenharmony_ci case PSERIES_HP_ELOG_RESOURCE_PMEM: 36262306a36Sopenharmony_ci rc = dlpar_hp_pmem(hp_elog); 36362306a36Sopenharmony_ci break; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci default: 36662306a36Sopenharmony_ci pr_warn_ratelimited("Invalid resource (%d) specified\n", 36762306a36Sopenharmony_ci hp_elog->resource); 36862306a36Sopenharmony_ci rc = -EINVAL; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci return rc; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic void pseries_hp_work_fn(struct work_struct *work) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct pseries_hp_work *hp_work = 37762306a36Sopenharmony_ci container_of(work, struct pseries_hp_work, work); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci handle_dlpar_errorlog(hp_work->errlog); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci kfree(hp_work->errlog); 38262306a36Sopenharmony_ci kfree(work); 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_civoid queue_hotplug_event(struct pseries_hp_errorlog *hp_errlog) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct pseries_hp_work *work; 38862306a36Sopenharmony_ci struct pseries_hp_errorlog *hp_errlog_copy; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci hp_errlog_copy = kmemdup(hp_errlog, sizeof(*hp_errlog), GFP_ATOMIC); 39162306a36Sopenharmony_ci if (!hp_errlog_copy) 39262306a36Sopenharmony_ci return; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci work = kmalloc(sizeof(struct pseries_hp_work), GFP_ATOMIC); 39562306a36Sopenharmony_ci if (work) { 39662306a36Sopenharmony_ci INIT_WORK((struct work_struct *)work, pseries_hp_work_fn); 39762306a36Sopenharmony_ci work->errlog = hp_errlog_copy; 39862306a36Sopenharmony_ci queue_work(pseries_hp_wq, (struct work_struct *)work); 39962306a36Sopenharmony_ci } else { 40062306a36Sopenharmony_ci kfree(hp_errlog_copy); 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic int dlpar_parse_resource(char **cmd, struct pseries_hp_errorlog *hp_elog) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci char *arg; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci arg = strsep(cmd, " "); 40962306a36Sopenharmony_ci if (!arg) 41062306a36Sopenharmony_ci return -EINVAL; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (sysfs_streq(arg, "memory")) { 41362306a36Sopenharmony_ci hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_MEM; 41462306a36Sopenharmony_ci } else if (sysfs_streq(arg, "cpu")) { 41562306a36Sopenharmony_ci hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_CPU; 41662306a36Sopenharmony_ci } else { 41762306a36Sopenharmony_ci pr_err("Invalid resource specified.\n"); 41862306a36Sopenharmony_ci return -EINVAL; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return 0; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic int dlpar_parse_action(char **cmd, struct pseries_hp_errorlog *hp_elog) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci char *arg; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci arg = strsep(cmd, " "); 42962306a36Sopenharmony_ci if (!arg) 43062306a36Sopenharmony_ci return -EINVAL; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (sysfs_streq(arg, "add")) { 43362306a36Sopenharmony_ci hp_elog->action = PSERIES_HP_ELOG_ACTION_ADD; 43462306a36Sopenharmony_ci } else if (sysfs_streq(arg, "remove")) { 43562306a36Sopenharmony_ci hp_elog->action = PSERIES_HP_ELOG_ACTION_REMOVE; 43662306a36Sopenharmony_ci } else { 43762306a36Sopenharmony_ci pr_err("Invalid action specified.\n"); 43862306a36Sopenharmony_ci return -EINVAL; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic int dlpar_parse_id_type(char **cmd, struct pseries_hp_errorlog *hp_elog) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci char *arg; 44762306a36Sopenharmony_ci u32 count, index; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci arg = strsep(cmd, " "); 45062306a36Sopenharmony_ci if (!arg) 45162306a36Sopenharmony_ci return -EINVAL; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (sysfs_streq(arg, "indexed-count")) { 45462306a36Sopenharmony_ci hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_IC; 45562306a36Sopenharmony_ci arg = strsep(cmd, " "); 45662306a36Sopenharmony_ci if (!arg) { 45762306a36Sopenharmony_ci pr_err("No DRC count specified.\n"); 45862306a36Sopenharmony_ci return -EINVAL; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (kstrtou32(arg, 0, &count)) { 46262306a36Sopenharmony_ci pr_err("Invalid DRC count specified.\n"); 46362306a36Sopenharmony_ci return -EINVAL; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci arg = strsep(cmd, " "); 46762306a36Sopenharmony_ci if (!arg) { 46862306a36Sopenharmony_ci pr_err("No DRC Index specified.\n"); 46962306a36Sopenharmony_ci return -EINVAL; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (kstrtou32(arg, 0, &index)) { 47362306a36Sopenharmony_ci pr_err("Invalid DRC Index specified.\n"); 47462306a36Sopenharmony_ci return -EINVAL; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci hp_elog->_drc_u.ic.count = cpu_to_be32(count); 47862306a36Sopenharmony_ci hp_elog->_drc_u.ic.index = cpu_to_be32(index); 47962306a36Sopenharmony_ci } else if (sysfs_streq(arg, "index")) { 48062306a36Sopenharmony_ci hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_INDEX; 48162306a36Sopenharmony_ci arg = strsep(cmd, " "); 48262306a36Sopenharmony_ci if (!arg) { 48362306a36Sopenharmony_ci pr_err("No DRC Index specified.\n"); 48462306a36Sopenharmony_ci return -EINVAL; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (kstrtou32(arg, 0, &index)) { 48862306a36Sopenharmony_ci pr_err("Invalid DRC Index specified.\n"); 48962306a36Sopenharmony_ci return -EINVAL; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci hp_elog->_drc_u.drc_index = cpu_to_be32(index); 49362306a36Sopenharmony_ci } else if (sysfs_streq(arg, "count")) { 49462306a36Sopenharmony_ci hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_COUNT; 49562306a36Sopenharmony_ci arg = strsep(cmd, " "); 49662306a36Sopenharmony_ci if (!arg) { 49762306a36Sopenharmony_ci pr_err("No DRC count specified.\n"); 49862306a36Sopenharmony_ci return -EINVAL; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (kstrtou32(arg, 0, &count)) { 50262306a36Sopenharmony_ci pr_err("Invalid DRC count specified.\n"); 50362306a36Sopenharmony_ci return -EINVAL; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci hp_elog->_drc_u.drc_count = cpu_to_be32(count); 50762306a36Sopenharmony_ci } else { 50862306a36Sopenharmony_ci pr_err("Invalid id_type specified.\n"); 50962306a36Sopenharmony_ci return -EINVAL; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci return 0; 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic ssize_t dlpar_store(const struct class *class, const struct class_attribute *attr, 51662306a36Sopenharmony_ci const char *buf, size_t count) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci struct pseries_hp_errorlog hp_elog; 51962306a36Sopenharmony_ci char *argbuf; 52062306a36Sopenharmony_ci char *args; 52162306a36Sopenharmony_ci int rc; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci args = argbuf = kstrdup(buf, GFP_KERNEL); 52462306a36Sopenharmony_ci if (!argbuf) 52562306a36Sopenharmony_ci return -ENOMEM; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* 52862306a36Sopenharmony_ci * Parse out the request from the user, this will be in the form: 52962306a36Sopenharmony_ci * <resource> <action> <id_type> <id> 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_ci rc = dlpar_parse_resource(&args, &hp_elog); 53262306a36Sopenharmony_ci if (rc) 53362306a36Sopenharmony_ci goto dlpar_store_out; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci rc = dlpar_parse_action(&args, &hp_elog); 53662306a36Sopenharmony_ci if (rc) 53762306a36Sopenharmony_ci goto dlpar_store_out; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci rc = dlpar_parse_id_type(&args, &hp_elog); 54062306a36Sopenharmony_ci if (rc) 54162306a36Sopenharmony_ci goto dlpar_store_out; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci rc = handle_dlpar_errorlog(&hp_elog); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cidlpar_store_out: 54662306a36Sopenharmony_ci kfree(argbuf); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if (rc) 54962306a36Sopenharmony_ci pr_err("Could not handle DLPAR request \"%s\"\n", buf); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci return rc ? rc : count; 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic ssize_t dlpar_show(const struct class *class, const struct class_attribute *attr, 55562306a36Sopenharmony_ci char *buf) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci return sprintf(buf, "%s\n", "memory,cpu"); 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic CLASS_ATTR_RW(dlpar); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ciint __init dlpar_workqueue_init(void) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci if (pseries_hp_wq) 56562306a36Sopenharmony_ci return 0; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci pseries_hp_wq = alloc_ordered_workqueue("pseries hotplug workqueue", 0); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci return pseries_hp_wq ? 0 : -ENOMEM; 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic int __init dlpar_sysfs_init(void) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci int rc; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci rc = dlpar_workqueue_init(); 57762306a36Sopenharmony_ci if (rc) 57862306a36Sopenharmony_ci return rc; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci return sysfs_create_file(kernel_kobj, &class_attr_dlpar.attr); 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_cimachine_device_initcall(pseries, dlpar_sysfs_init); 58362306a36Sopenharmony_ci 584