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 13ab519a01SNathan Fontenot #include <linux/kernel.h> 14ab519a01SNathan Fontenot #include <linux/kref.h> 15ab519a01SNathan Fontenot #include <linux/notifier.h> 16ab519a01SNathan Fontenot #include <linux/proc_fs.h> 17ab519a01SNathan Fontenot #include <linux/spinlock.h> 18ab519a01SNathan Fontenot #include <linux/cpu.h> 19ab519a01SNathan Fontenot 20ab519a01SNathan Fontenot #include <asm/prom.h> 21ab519a01SNathan Fontenot #include <asm/machdep.h> 22ab519a01SNathan Fontenot #include <asm/uaccess.h> 23ab519a01SNathan Fontenot #include <asm/rtas.h> 24ab519a01SNathan Fontenot #include <asm/pSeries_reconfig.h> 25ab519a01SNathan Fontenot 26ab519a01SNathan Fontenot struct cc_workarea { 27ab519a01SNathan Fontenot u32 drc_index; 28ab519a01SNathan Fontenot u32 zero; 29ab519a01SNathan Fontenot u32 name_offset; 30ab519a01SNathan Fontenot u32 prop_length; 31ab519a01SNathan Fontenot u32 prop_offset; 32ab519a01SNathan Fontenot }; 33ab519a01SNathan Fontenot 34ab519a01SNathan Fontenot static void dlpar_free_cc_property(struct property *prop) 35ab519a01SNathan Fontenot { 36ab519a01SNathan Fontenot kfree(prop->name); 37ab519a01SNathan Fontenot kfree(prop->value); 38ab519a01SNathan Fontenot kfree(prop); 39ab519a01SNathan Fontenot } 40ab519a01SNathan Fontenot 41ab519a01SNathan Fontenot static struct property *dlpar_parse_cc_property(struct cc_workarea *ccwa) 42ab519a01SNathan Fontenot { 43ab519a01SNathan Fontenot struct property *prop; 44ab519a01SNathan Fontenot char *name; 45ab519a01SNathan Fontenot char *value; 46ab519a01SNathan Fontenot 47ab519a01SNathan Fontenot prop = kzalloc(sizeof(*prop), GFP_KERNEL); 48ab519a01SNathan Fontenot if (!prop) 49ab519a01SNathan Fontenot return NULL; 50ab519a01SNathan Fontenot 51ab519a01SNathan Fontenot name = (char *)ccwa + ccwa->name_offset; 52ab519a01SNathan Fontenot prop->name = kstrdup(name, GFP_KERNEL); 53ab519a01SNathan Fontenot 54ab519a01SNathan Fontenot prop->length = ccwa->prop_length; 55ab519a01SNathan Fontenot value = (char *)ccwa + ccwa->prop_offset; 56ab519a01SNathan Fontenot prop->value = kzalloc(prop->length, GFP_KERNEL); 57ab519a01SNathan Fontenot if (!prop->value) { 58ab519a01SNathan Fontenot dlpar_free_cc_property(prop); 59ab519a01SNathan Fontenot return NULL; 60ab519a01SNathan Fontenot } 61ab519a01SNathan Fontenot 62ab519a01SNathan Fontenot memcpy(prop->value, value, prop->length); 63ab519a01SNathan Fontenot return prop; 64ab519a01SNathan Fontenot } 65ab519a01SNathan Fontenot 66ab519a01SNathan Fontenot static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa) 67ab519a01SNathan Fontenot { 68ab519a01SNathan Fontenot struct device_node *dn; 69ab519a01SNathan Fontenot char *name; 70ab519a01SNathan Fontenot 71ab519a01SNathan Fontenot dn = kzalloc(sizeof(*dn), GFP_KERNEL); 72ab519a01SNathan Fontenot if (!dn) 73ab519a01SNathan Fontenot return NULL; 74ab519a01SNathan Fontenot 75ab519a01SNathan Fontenot /* The configure connector reported name does not contain a 76ab519a01SNathan Fontenot * preceeding '/', so we allocate a buffer large enough to 77ab519a01SNathan Fontenot * prepend this to the full_name. 78ab519a01SNathan Fontenot */ 79ab519a01SNathan Fontenot name = (char *)ccwa + ccwa->name_offset; 80ab519a01SNathan Fontenot dn->full_name = kmalloc(strlen(name) + 2, GFP_KERNEL); 81ab519a01SNathan Fontenot if (!dn->full_name) { 82ab519a01SNathan Fontenot kfree(dn); 83ab519a01SNathan Fontenot return NULL; 84ab519a01SNathan Fontenot } 85ab519a01SNathan Fontenot 86ab519a01SNathan Fontenot sprintf(dn->full_name, "/%s", name); 87ab519a01SNathan Fontenot return dn; 88ab519a01SNathan Fontenot } 89ab519a01SNathan Fontenot 90ab519a01SNathan Fontenot static void dlpar_free_one_cc_node(struct device_node *dn) 91ab519a01SNathan Fontenot { 92ab519a01SNathan Fontenot struct property *prop; 93ab519a01SNathan Fontenot 94ab519a01SNathan Fontenot while (dn->properties) { 95ab519a01SNathan Fontenot prop = dn->properties; 96ab519a01SNathan Fontenot dn->properties = prop->next; 97ab519a01SNathan Fontenot dlpar_free_cc_property(prop); 98ab519a01SNathan Fontenot } 99ab519a01SNathan Fontenot 100ab519a01SNathan Fontenot kfree(dn->full_name); 101ab519a01SNathan Fontenot kfree(dn); 102ab519a01SNathan Fontenot } 103ab519a01SNathan Fontenot 104ab519a01SNathan Fontenot static void dlpar_free_cc_nodes(struct device_node *dn) 105ab519a01SNathan Fontenot { 106ab519a01SNathan Fontenot if (dn->child) 107ab519a01SNathan Fontenot dlpar_free_cc_nodes(dn->child); 108ab519a01SNathan Fontenot 109ab519a01SNathan Fontenot if (dn->sibling) 110ab519a01SNathan Fontenot dlpar_free_cc_nodes(dn->sibling); 111ab519a01SNathan Fontenot 112ab519a01SNathan Fontenot dlpar_free_one_cc_node(dn); 113ab519a01SNathan Fontenot } 114ab519a01SNathan Fontenot 115ab519a01SNathan Fontenot #define NEXT_SIBLING 1 116ab519a01SNathan Fontenot #define NEXT_CHILD 2 117ab519a01SNathan Fontenot #define NEXT_PROPERTY 3 118ab519a01SNathan Fontenot #define PREV_PARENT 4 119ab519a01SNathan Fontenot #define MORE_MEMORY 5 120ab519a01SNathan Fontenot #define CALL_AGAIN -2 121ab519a01SNathan Fontenot #define ERR_CFG_USE -9003 122ab519a01SNathan Fontenot 123ab519a01SNathan Fontenot struct device_node *dlpar_configure_connector(u32 drc_index) 124ab519a01SNathan Fontenot { 125ab519a01SNathan Fontenot struct device_node *dn; 126ab519a01SNathan Fontenot struct device_node *first_dn = NULL; 127ab519a01SNathan Fontenot struct device_node *last_dn = NULL; 128ab519a01SNathan Fontenot struct property *property; 129ab519a01SNathan Fontenot struct property *last_property = NULL; 130ab519a01SNathan Fontenot struct cc_workarea *ccwa; 131ab519a01SNathan Fontenot int cc_token; 132ab519a01SNathan Fontenot int rc; 133ab519a01SNathan Fontenot 134ab519a01SNathan Fontenot cc_token = rtas_token("ibm,configure-connector"); 135ab519a01SNathan Fontenot if (cc_token == RTAS_UNKNOWN_SERVICE) 136ab519a01SNathan Fontenot return NULL; 137ab519a01SNathan Fontenot 138ab519a01SNathan Fontenot spin_lock(&rtas_data_buf_lock); 139ab519a01SNathan Fontenot ccwa = (struct cc_workarea *)&rtas_data_buf[0]; 140ab519a01SNathan Fontenot ccwa->drc_index = drc_index; 141ab519a01SNathan Fontenot ccwa->zero = 0; 142ab519a01SNathan Fontenot 143ab519a01SNathan Fontenot rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL); 144ab519a01SNathan Fontenot while (rc) { 145ab519a01SNathan Fontenot switch (rc) { 146ab519a01SNathan Fontenot case NEXT_SIBLING: 147ab519a01SNathan Fontenot dn = dlpar_parse_cc_node(ccwa); 148ab519a01SNathan Fontenot if (!dn) 149ab519a01SNathan Fontenot goto cc_error; 150ab519a01SNathan Fontenot 151ab519a01SNathan Fontenot dn->parent = last_dn->parent; 152ab519a01SNathan Fontenot last_dn->sibling = dn; 153ab519a01SNathan Fontenot last_dn = dn; 154ab519a01SNathan Fontenot break; 155ab519a01SNathan Fontenot 156ab519a01SNathan Fontenot case NEXT_CHILD: 157ab519a01SNathan Fontenot dn = dlpar_parse_cc_node(ccwa); 158ab519a01SNathan Fontenot if (!dn) 159ab519a01SNathan Fontenot goto cc_error; 160ab519a01SNathan Fontenot 161ab519a01SNathan Fontenot if (!first_dn) 162ab519a01SNathan Fontenot first_dn = dn; 163ab519a01SNathan Fontenot else { 164ab519a01SNathan Fontenot dn->parent = last_dn; 165ab519a01SNathan Fontenot if (last_dn) 166ab519a01SNathan Fontenot last_dn->child = dn; 167ab519a01SNathan Fontenot } 168ab519a01SNathan Fontenot 169ab519a01SNathan Fontenot last_dn = dn; 170ab519a01SNathan Fontenot break; 171ab519a01SNathan Fontenot 172ab519a01SNathan Fontenot case NEXT_PROPERTY: 173ab519a01SNathan Fontenot property = dlpar_parse_cc_property(ccwa); 174ab519a01SNathan Fontenot if (!property) 175ab519a01SNathan Fontenot goto cc_error; 176ab519a01SNathan Fontenot 177ab519a01SNathan Fontenot if (!last_dn->properties) 178ab519a01SNathan Fontenot last_dn->properties = property; 179ab519a01SNathan Fontenot else 180ab519a01SNathan Fontenot last_property->next = property; 181ab519a01SNathan Fontenot 182ab519a01SNathan Fontenot last_property = property; 183ab519a01SNathan Fontenot break; 184ab519a01SNathan Fontenot 185ab519a01SNathan Fontenot case PREV_PARENT: 186ab519a01SNathan Fontenot last_dn = last_dn->parent; 187ab519a01SNathan Fontenot break; 188ab519a01SNathan Fontenot 189ab519a01SNathan Fontenot case CALL_AGAIN: 190ab519a01SNathan Fontenot break; 191ab519a01SNathan Fontenot 192ab519a01SNathan Fontenot case MORE_MEMORY: 193ab519a01SNathan Fontenot case ERR_CFG_USE: 194ab519a01SNathan Fontenot default: 195ab519a01SNathan Fontenot printk(KERN_ERR "Unexpected Error (%d) " 196ab519a01SNathan Fontenot "returned from configure-connector\n", rc); 197ab519a01SNathan Fontenot goto cc_error; 198ab519a01SNathan Fontenot } 199ab519a01SNathan Fontenot 200ab519a01SNathan Fontenot rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL); 201ab519a01SNathan Fontenot } 202ab519a01SNathan Fontenot 203ab519a01SNathan Fontenot spin_unlock(&rtas_data_buf_lock); 204ab519a01SNathan Fontenot return first_dn; 205ab519a01SNathan Fontenot 206ab519a01SNathan Fontenot cc_error: 207ab519a01SNathan Fontenot if (first_dn) 208ab519a01SNathan Fontenot dlpar_free_cc_nodes(first_dn); 209ab519a01SNathan Fontenot spin_unlock(&rtas_data_buf_lock); 210ab519a01SNathan Fontenot return NULL; 211ab519a01SNathan Fontenot } 212ab519a01SNathan Fontenot 213ab519a01SNathan Fontenot static struct device_node *derive_parent(const char *path) 214ab519a01SNathan Fontenot { 215ab519a01SNathan Fontenot struct device_node *parent; 216ab519a01SNathan Fontenot char *last_slash; 217ab519a01SNathan Fontenot 218ab519a01SNathan Fontenot last_slash = strrchr(path, '/'); 219ab519a01SNathan Fontenot if (last_slash == path) { 220ab519a01SNathan Fontenot parent = of_find_node_by_path("/"); 221ab519a01SNathan Fontenot } else { 222ab519a01SNathan Fontenot char *parent_path; 223ab519a01SNathan Fontenot int parent_path_len = last_slash - path + 1; 224ab519a01SNathan Fontenot parent_path = kmalloc(parent_path_len, GFP_KERNEL); 225ab519a01SNathan Fontenot if (!parent_path) 226ab519a01SNathan Fontenot return NULL; 227ab519a01SNathan Fontenot 228ab519a01SNathan Fontenot strlcpy(parent_path, path, parent_path_len); 229ab519a01SNathan Fontenot parent = of_find_node_by_path(parent_path); 230ab519a01SNathan Fontenot kfree(parent_path); 231ab519a01SNathan Fontenot } 232ab519a01SNathan Fontenot 233ab519a01SNathan Fontenot return parent; 234ab519a01SNathan Fontenot } 235ab519a01SNathan Fontenot 236ab519a01SNathan Fontenot int dlpar_attach_node(struct device_node *dn) 237ab519a01SNathan Fontenot { 238ab519a01SNathan Fontenot struct proc_dir_entry *ent; 239ab519a01SNathan Fontenot int rc; 240ab519a01SNathan Fontenot 241ab519a01SNathan Fontenot of_node_set_flag(dn, OF_DYNAMIC); 242ab519a01SNathan Fontenot kref_init(&dn->kref); 243ab519a01SNathan Fontenot dn->parent = derive_parent(dn->full_name); 244ab519a01SNathan Fontenot if (!dn->parent) 245ab519a01SNathan Fontenot return -ENOMEM; 246ab519a01SNathan Fontenot 247ab519a01SNathan Fontenot rc = blocking_notifier_call_chain(&pSeries_reconfig_chain, 248ab519a01SNathan Fontenot PSERIES_RECONFIG_ADD, dn); 249ab519a01SNathan Fontenot if (rc == NOTIFY_BAD) { 250ab519a01SNathan Fontenot printk(KERN_ERR "Failed to add device node %s\n", 251ab519a01SNathan Fontenot dn->full_name); 252ab519a01SNathan Fontenot return -ENOMEM; /* For now, safe to assume kmalloc failure */ 253ab519a01SNathan Fontenot } 254ab519a01SNathan Fontenot 255ab519a01SNathan Fontenot of_attach_node(dn); 256ab519a01SNathan Fontenot 257ab519a01SNathan Fontenot #ifdef CONFIG_PROC_DEVICETREE 258ab519a01SNathan Fontenot ent = proc_mkdir(strrchr(dn->full_name, '/') + 1, dn->parent->pde); 259ab519a01SNathan Fontenot if (ent) 260ab519a01SNathan Fontenot proc_device_tree_add_node(dn, ent); 261ab519a01SNathan Fontenot #endif 262ab519a01SNathan Fontenot 263ab519a01SNathan Fontenot of_node_put(dn->parent); 264ab519a01SNathan Fontenot return 0; 265ab519a01SNathan Fontenot } 266ab519a01SNathan Fontenot 267ab519a01SNathan Fontenot int dlpar_detach_node(struct device_node *dn) 268ab519a01SNathan Fontenot { 269ab519a01SNathan Fontenot struct device_node *parent = dn->parent; 270ab519a01SNathan Fontenot struct property *prop = dn->properties; 271ab519a01SNathan Fontenot 272ab519a01SNathan Fontenot #ifdef CONFIG_PROC_DEVICETREE 273ab519a01SNathan Fontenot while (prop) { 274ab519a01SNathan Fontenot remove_proc_entry(prop->name, dn->pde); 275ab519a01SNathan Fontenot prop = prop->next; 276ab519a01SNathan Fontenot } 277ab519a01SNathan Fontenot 278ab519a01SNathan Fontenot if (dn->pde) 279ab519a01SNathan Fontenot remove_proc_entry(dn->pde->name, parent->pde); 280ab519a01SNathan Fontenot #endif 281ab519a01SNathan Fontenot 282ab519a01SNathan Fontenot blocking_notifier_call_chain(&pSeries_reconfig_chain, 283ab519a01SNathan Fontenot PSERIES_RECONFIG_REMOVE, dn); 284ab519a01SNathan Fontenot of_detach_node(dn); 285ab519a01SNathan Fontenot of_node_put(dn); /* Must decrement the refcount */ 286ab519a01SNathan Fontenot 287ab519a01SNathan Fontenot return 0; 288ab519a01SNathan Fontenot } 289ab519a01SNathan Fontenot 290ab519a01SNathan Fontenot #define DR_ENTITY_SENSE 9003 291ab519a01SNathan Fontenot #define DR_ENTITY_PRESENT 1 292ab519a01SNathan Fontenot #define DR_ENTITY_UNUSABLE 2 293ab519a01SNathan Fontenot #define ALLOCATION_STATE 9003 294ab519a01SNathan Fontenot #define ALLOC_UNUSABLE 0 295ab519a01SNathan Fontenot #define ALLOC_USABLE 1 296ab519a01SNathan Fontenot #define ISOLATION_STATE 9001 297ab519a01SNathan Fontenot #define ISOLATE 0 298ab519a01SNathan Fontenot #define UNISOLATE 1 299ab519a01SNathan Fontenot 300ab519a01SNathan Fontenot int dlpar_acquire_drc(u32 drc_index) 301ab519a01SNathan Fontenot { 302ab519a01SNathan Fontenot int dr_status, rc; 303ab519a01SNathan Fontenot 304ab519a01SNathan Fontenot rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status, 305ab519a01SNathan Fontenot DR_ENTITY_SENSE, drc_index); 306ab519a01SNathan Fontenot if (rc || dr_status != DR_ENTITY_UNUSABLE) 307ab519a01SNathan Fontenot return -1; 308ab519a01SNathan Fontenot 309ab519a01SNathan Fontenot rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE); 310ab519a01SNathan Fontenot if (rc) 311ab519a01SNathan Fontenot return rc; 312ab519a01SNathan Fontenot 313ab519a01SNathan Fontenot rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 314ab519a01SNathan Fontenot if (rc) { 315ab519a01SNathan Fontenot rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 316ab519a01SNathan Fontenot return rc; 317ab519a01SNathan Fontenot } 318ab519a01SNathan Fontenot 319ab519a01SNathan Fontenot return 0; 320ab519a01SNathan Fontenot } 321ab519a01SNathan Fontenot 322ab519a01SNathan Fontenot int dlpar_release_drc(u32 drc_index) 323ab519a01SNathan Fontenot { 324ab519a01SNathan Fontenot int dr_status, rc; 325ab519a01SNathan Fontenot 326ab519a01SNathan Fontenot rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status, 327ab519a01SNathan Fontenot DR_ENTITY_SENSE, drc_index); 328ab519a01SNathan Fontenot if (rc || dr_status != DR_ENTITY_PRESENT) 329ab519a01SNathan Fontenot return -1; 330ab519a01SNathan Fontenot 331ab519a01SNathan Fontenot rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE); 332ab519a01SNathan Fontenot if (rc) 333ab519a01SNathan Fontenot return rc; 334ab519a01SNathan Fontenot 335ab519a01SNathan Fontenot rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 336ab519a01SNathan Fontenot if (rc) { 337ab519a01SNathan Fontenot rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 338ab519a01SNathan Fontenot return rc; 339ab519a01SNathan Fontenot } 340ab519a01SNathan Fontenot 341ab519a01SNathan Fontenot return 0; 342ab519a01SNathan Fontenot } 343ab519a01SNathan Fontenot 344*1a8061c4SNathan Fontenot #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE 345ab519a01SNathan Fontenot 346*1a8061c4SNathan Fontenot static ssize_t dlpar_cpu_probe(const char *buf, size_t count) 347*1a8061c4SNathan Fontenot { 348*1a8061c4SNathan Fontenot struct device_node *dn; 349*1a8061c4SNathan Fontenot unsigned long drc_index; 350*1a8061c4SNathan Fontenot char *cpu_name; 351*1a8061c4SNathan Fontenot int rc; 352*1a8061c4SNathan Fontenot 353*1a8061c4SNathan Fontenot rc = strict_strtoul(buf, 0, &drc_index); 354*1a8061c4SNathan Fontenot if (rc) 355*1a8061c4SNathan Fontenot return -EINVAL; 356*1a8061c4SNathan Fontenot 357*1a8061c4SNathan Fontenot dn = dlpar_configure_connector(drc_index); 358*1a8061c4SNathan Fontenot if (!dn) 359*1a8061c4SNathan Fontenot return -EINVAL; 360*1a8061c4SNathan Fontenot 361*1a8061c4SNathan Fontenot /* configure-connector reports cpus as living in the base 362*1a8061c4SNathan Fontenot * directory of the device tree. CPUs actually live in the 363*1a8061c4SNathan Fontenot * cpus directory so we need to fixup the full_name. 364*1a8061c4SNathan Fontenot */ 365*1a8061c4SNathan Fontenot cpu_name = kzalloc(strlen(dn->full_name) + strlen("/cpus") + 1, 366*1a8061c4SNathan Fontenot GFP_KERNEL); 367*1a8061c4SNathan Fontenot if (!cpu_name) { 368*1a8061c4SNathan Fontenot dlpar_free_cc_nodes(dn); 369*1a8061c4SNathan Fontenot return -ENOMEM; 370*1a8061c4SNathan Fontenot } 371*1a8061c4SNathan Fontenot 372*1a8061c4SNathan Fontenot sprintf(cpu_name, "/cpus%s", dn->full_name); 373*1a8061c4SNathan Fontenot kfree(dn->full_name); 374*1a8061c4SNathan Fontenot dn->full_name = cpu_name; 375*1a8061c4SNathan Fontenot 376*1a8061c4SNathan Fontenot rc = dlpar_acquire_drc(drc_index); 377*1a8061c4SNathan Fontenot if (rc) { 378*1a8061c4SNathan Fontenot dlpar_free_cc_nodes(dn); 379*1a8061c4SNathan Fontenot return -EINVAL; 380*1a8061c4SNathan Fontenot } 381*1a8061c4SNathan Fontenot 382*1a8061c4SNathan Fontenot rc = dlpar_attach_node(dn); 383*1a8061c4SNathan Fontenot if (rc) { 384*1a8061c4SNathan Fontenot dlpar_release_drc(drc_index); 385*1a8061c4SNathan Fontenot dlpar_free_cc_nodes(dn); 386*1a8061c4SNathan Fontenot } 387*1a8061c4SNathan Fontenot 388*1a8061c4SNathan Fontenot return rc ? rc : count; 389*1a8061c4SNathan Fontenot } 390*1a8061c4SNathan Fontenot 391*1a8061c4SNathan Fontenot static ssize_t dlpar_cpu_release(const char *buf, size_t count) 392*1a8061c4SNathan Fontenot { 393*1a8061c4SNathan Fontenot struct device_node *dn; 394*1a8061c4SNathan Fontenot const u32 *drc_index; 395*1a8061c4SNathan Fontenot int rc; 396*1a8061c4SNathan Fontenot 397*1a8061c4SNathan Fontenot dn = of_find_node_by_path(buf); 398*1a8061c4SNathan Fontenot if (!dn) 399*1a8061c4SNathan Fontenot return -EINVAL; 400*1a8061c4SNathan Fontenot 401*1a8061c4SNathan Fontenot drc_index = of_get_property(dn, "ibm,my-drc-index", NULL); 402*1a8061c4SNathan Fontenot if (!drc_index) { 403*1a8061c4SNathan Fontenot of_node_put(dn); 404*1a8061c4SNathan Fontenot return -EINVAL; 405*1a8061c4SNathan Fontenot } 406*1a8061c4SNathan Fontenot 407*1a8061c4SNathan Fontenot rc = dlpar_release_drc(*drc_index); 408*1a8061c4SNathan Fontenot if (rc) { 409*1a8061c4SNathan Fontenot of_node_put(dn); 410*1a8061c4SNathan Fontenot return -EINVAL; 411*1a8061c4SNathan Fontenot } 412*1a8061c4SNathan Fontenot 413*1a8061c4SNathan Fontenot rc = dlpar_detach_node(dn); 414*1a8061c4SNathan Fontenot if (rc) { 415*1a8061c4SNathan Fontenot dlpar_acquire_drc(*drc_index); 416*1a8061c4SNathan Fontenot return rc; 417*1a8061c4SNathan Fontenot } 418*1a8061c4SNathan Fontenot 419*1a8061c4SNathan Fontenot of_node_put(dn); 420*1a8061c4SNathan Fontenot return count; 421*1a8061c4SNathan Fontenot } 422*1a8061c4SNathan Fontenot 423*1a8061c4SNathan Fontenot static int __init pseries_dlpar_init(void) 424*1a8061c4SNathan Fontenot { 425*1a8061c4SNathan Fontenot ppc_md.cpu_probe = dlpar_cpu_probe; 426*1a8061c4SNathan Fontenot ppc_md.cpu_release = dlpar_cpu_release; 427*1a8061c4SNathan Fontenot 428*1a8061c4SNathan Fontenot return 0; 429*1a8061c4SNathan Fontenot } 430*1a8061c4SNathan Fontenot machine_device_initcall(pseries, pseries_dlpar_init); 431*1a8061c4SNathan Fontenot 432*1a8061c4SNathan Fontenot #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ 433