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/notifier.h> 15ab519a01SNathan Fontenot #include <linux/spinlock.h> 16ab519a01SNathan Fontenot #include <linux/cpu.h> 175a0e3ad6STejun Heo #include <linux/slab.h> 181cf3d8b3SNathan Fontenot #include <linux/of.h> 19b6db63d1SGautham R Shenoy #include "offline_states.h" 20ab519a01SNathan Fontenot 21ab519a01SNathan Fontenot #include <asm/prom.h> 22ab519a01SNathan Fontenot #include <asm/machdep.h> 23ab519a01SNathan Fontenot #include <asm/uaccess.h> 24ab519a01SNathan Fontenot #include <asm/rtas.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 3420648974SNathan Fontenot 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; 56e72ed6b5SNishanth Aravamudan prop->value = kmemdup(value, 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 return prop; 63ab519a01SNathan Fontenot } 64ab519a01SNathan Fontenot 658d5ff320STyrel Datwyler static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa, 668d5ff320STyrel Datwyler const char *path) 67ab519a01SNathan Fontenot { 68ab519a01SNathan Fontenot struct device_node *dn; 69ab519a01SNathan Fontenot char *name; 70ab519a01SNathan Fontenot 718d5ff320STyrel Datwyler /* If parent node path is "/" advance path to NULL terminator to 728d5ff320STyrel Datwyler * prevent double leading slashs in full_name. 738d5ff320STyrel Datwyler */ 748d5ff320STyrel Datwyler if (!path[1]) 758d5ff320STyrel Datwyler path++; 768d5ff320STyrel Datwyler 77ab519a01SNathan Fontenot dn = kzalloc(sizeof(*dn), GFP_KERNEL); 78ab519a01SNathan Fontenot if (!dn) 79ab519a01SNathan Fontenot return NULL; 80ab519a01SNathan Fontenot 81ab519a01SNathan Fontenot name = (char *)ccwa + ccwa->name_offset; 828d5ff320STyrel Datwyler dn->full_name = kasprintf(GFP_KERNEL, "%s/%s", path, name); 83ab519a01SNathan Fontenot if (!dn->full_name) { 84ab519a01SNathan Fontenot kfree(dn); 85ab519a01SNathan Fontenot return NULL; 86ab519a01SNathan Fontenot } 87ab519a01SNathan Fontenot 881578cb76STyrel Datwyler of_node_set_flag(dn, OF_DYNAMIC); 89*97a9a717STyrel Datwyler of_node_init(dn); 901578cb76STyrel Datwyler 91ab519a01SNathan Fontenot return dn; 92ab519a01SNathan Fontenot } 93ab519a01SNathan Fontenot 94ab519a01SNathan Fontenot static void dlpar_free_one_cc_node(struct device_node *dn) 95ab519a01SNathan Fontenot { 96ab519a01SNathan Fontenot struct property *prop; 97ab519a01SNathan Fontenot 98ab519a01SNathan Fontenot while (dn->properties) { 99ab519a01SNathan Fontenot prop = dn->properties; 100ab519a01SNathan Fontenot dn->properties = prop->next; 101ab519a01SNathan Fontenot dlpar_free_cc_property(prop); 102ab519a01SNathan Fontenot } 103ab519a01SNathan Fontenot 104ab519a01SNathan Fontenot kfree(dn->full_name); 105ab519a01SNathan Fontenot kfree(dn); 106ab519a01SNathan Fontenot } 107ab519a01SNathan Fontenot 10820648974SNathan Fontenot void dlpar_free_cc_nodes(struct device_node *dn) 109ab519a01SNathan Fontenot { 110ab519a01SNathan Fontenot if (dn->child) 111ab519a01SNathan Fontenot dlpar_free_cc_nodes(dn->child); 112ab519a01SNathan Fontenot 113ab519a01SNathan Fontenot if (dn->sibling) 114ab519a01SNathan Fontenot dlpar_free_cc_nodes(dn->sibling); 115ab519a01SNathan Fontenot 116ab519a01SNathan Fontenot dlpar_free_one_cc_node(dn); 117ab519a01SNathan Fontenot } 118ab519a01SNathan Fontenot 1199c740025SAnton Blanchard #define COMPLETE 0 120ab519a01SNathan Fontenot #define NEXT_SIBLING 1 121ab519a01SNathan Fontenot #define NEXT_CHILD 2 122ab519a01SNathan Fontenot #define NEXT_PROPERTY 3 123ab519a01SNathan Fontenot #define PREV_PARENT 4 124ab519a01SNathan Fontenot #define MORE_MEMORY 5 125ab519a01SNathan Fontenot #define CALL_AGAIN -2 126ab519a01SNathan Fontenot #define ERR_CFG_USE -9003 127ab519a01SNathan Fontenot 1288d5ff320STyrel Datwyler struct device_node *dlpar_configure_connector(u32 drc_index, 1298d5ff320STyrel Datwyler struct device_node *parent) 130ab519a01SNathan Fontenot { 131ab519a01SNathan Fontenot struct device_node *dn; 132ab519a01SNathan Fontenot struct device_node *first_dn = NULL; 133ab519a01SNathan Fontenot struct device_node *last_dn = NULL; 134ab519a01SNathan Fontenot struct property *property; 135ab519a01SNathan Fontenot struct property *last_property = NULL; 136ab519a01SNathan Fontenot struct cc_workarea *ccwa; 13793f68f1eSNathan Fontenot char *data_buf; 1388d5ff320STyrel Datwyler const char *parent_path = parent->full_name; 139ab519a01SNathan Fontenot int cc_token; 14093f68f1eSNathan Fontenot int rc = -1; 141ab519a01SNathan Fontenot 142ab519a01SNathan Fontenot cc_token = rtas_token("ibm,configure-connector"); 143ab519a01SNathan Fontenot if (cc_token == RTAS_UNKNOWN_SERVICE) 144ab519a01SNathan Fontenot return NULL; 145ab519a01SNathan Fontenot 14693f68f1eSNathan Fontenot data_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL); 14793f68f1eSNathan Fontenot if (!data_buf) 14893f68f1eSNathan Fontenot return NULL; 14993f68f1eSNathan Fontenot 15093f68f1eSNathan Fontenot ccwa = (struct cc_workarea *)&data_buf[0]; 151ab519a01SNathan Fontenot ccwa->drc_index = drc_index; 152ab519a01SNathan Fontenot ccwa->zero = 0; 153ab519a01SNathan Fontenot 15493f68f1eSNathan Fontenot do { 15593f68f1eSNathan Fontenot /* Since we release the rtas_data_buf lock between configure 15693f68f1eSNathan Fontenot * connector calls we want to re-populate the rtas_data_buffer 15793f68f1eSNathan Fontenot * with the contents of the previous call. 15893f68f1eSNathan Fontenot */ 15993f68f1eSNathan Fontenot spin_lock(&rtas_data_buf_lock); 16093f68f1eSNathan Fontenot 16193f68f1eSNathan Fontenot memcpy(rtas_data_buf, data_buf, RTAS_DATA_BUF_SIZE); 162ab519a01SNathan Fontenot rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL); 16393f68f1eSNathan Fontenot memcpy(data_buf, rtas_data_buf, RTAS_DATA_BUF_SIZE); 16493f68f1eSNathan Fontenot 16593f68f1eSNathan Fontenot spin_unlock(&rtas_data_buf_lock); 16693f68f1eSNathan Fontenot 167ab519a01SNathan Fontenot switch (rc) { 1689c740025SAnton Blanchard case COMPLETE: 1699c740025SAnton Blanchard break; 1709c740025SAnton Blanchard 171ab519a01SNathan Fontenot case NEXT_SIBLING: 1728d5ff320STyrel Datwyler dn = dlpar_parse_cc_node(ccwa, parent_path); 173ab519a01SNathan Fontenot if (!dn) 174ab519a01SNathan Fontenot goto cc_error; 175ab519a01SNathan Fontenot 176ab519a01SNathan Fontenot dn->parent = last_dn->parent; 177ab519a01SNathan Fontenot last_dn->sibling = dn; 178ab519a01SNathan Fontenot last_dn = dn; 179ab519a01SNathan Fontenot break; 180ab519a01SNathan Fontenot 181ab519a01SNathan Fontenot case NEXT_CHILD: 1828d5ff320STyrel Datwyler if (first_dn) 1838d5ff320STyrel Datwyler parent_path = last_dn->full_name; 1848d5ff320STyrel Datwyler 1858d5ff320STyrel Datwyler dn = dlpar_parse_cc_node(ccwa, parent_path); 186ab519a01SNathan Fontenot if (!dn) 187ab519a01SNathan Fontenot goto cc_error; 188ab519a01SNathan Fontenot 1898d5ff320STyrel Datwyler if (!first_dn) { 1908d5ff320STyrel Datwyler dn->parent = parent; 191ab519a01SNathan Fontenot first_dn = dn; 1928d5ff320STyrel Datwyler } else { 193ab519a01SNathan Fontenot dn->parent = last_dn; 194ab519a01SNathan Fontenot if (last_dn) 195ab519a01SNathan Fontenot last_dn->child = dn; 196ab519a01SNathan Fontenot } 197ab519a01SNathan Fontenot 198ab519a01SNathan Fontenot last_dn = dn; 199ab519a01SNathan Fontenot break; 200ab519a01SNathan Fontenot 201ab519a01SNathan Fontenot case NEXT_PROPERTY: 202ab519a01SNathan Fontenot property = dlpar_parse_cc_property(ccwa); 203ab519a01SNathan Fontenot if (!property) 204ab519a01SNathan Fontenot goto cc_error; 205ab519a01SNathan Fontenot 206ab519a01SNathan Fontenot if (!last_dn->properties) 207ab519a01SNathan Fontenot last_dn->properties = property; 208ab519a01SNathan Fontenot else 209ab519a01SNathan Fontenot last_property->next = property; 210ab519a01SNathan Fontenot 211ab519a01SNathan Fontenot last_property = property; 212ab519a01SNathan Fontenot break; 213ab519a01SNathan Fontenot 214ab519a01SNathan Fontenot case PREV_PARENT: 215ab519a01SNathan Fontenot last_dn = last_dn->parent; 2168d5ff320STyrel Datwyler parent_path = last_dn->parent->full_name; 217ab519a01SNathan Fontenot break; 218ab519a01SNathan Fontenot 219ab519a01SNathan Fontenot case CALL_AGAIN: 220ab519a01SNathan Fontenot break; 221ab519a01SNathan Fontenot 222ab519a01SNathan Fontenot case MORE_MEMORY: 223ab519a01SNathan Fontenot case ERR_CFG_USE: 224ab519a01SNathan Fontenot default: 225ab519a01SNathan Fontenot printk(KERN_ERR "Unexpected Error (%d) " 226ab519a01SNathan Fontenot "returned from configure-connector\n", rc); 227ab519a01SNathan Fontenot goto cc_error; 228ab519a01SNathan Fontenot } 22993f68f1eSNathan Fontenot } while (rc); 230ab519a01SNathan Fontenot 231ab519a01SNathan Fontenot cc_error: 23293f68f1eSNathan Fontenot kfree(data_buf); 23393f68f1eSNathan Fontenot 23493f68f1eSNathan Fontenot if (rc) { 235ab519a01SNathan Fontenot if (first_dn) 236ab519a01SNathan Fontenot dlpar_free_cc_nodes(first_dn); 23793f68f1eSNathan Fontenot 238ab519a01SNathan Fontenot return NULL; 239ab519a01SNathan Fontenot } 240ab519a01SNathan Fontenot 24193f68f1eSNathan Fontenot return first_dn; 24293f68f1eSNathan Fontenot } 24393f68f1eSNathan Fontenot 244ab519a01SNathan Fontenot static struct device_node *derive_parent(const char *path) 245ab519a01SNathan Fontenot { 246ab519a01SNathan Fontenot struct device_node *parent; 247ab519a01SNathan Fontenot char *last_slash; 248ab519a01SNathan Fontenot 249ab519a01SNathan Fontenot last_slash = strrchr(path, '/'); 250ab519a01SNathan Fontenot if (last_slash == path) { 251ab519a01SNathan Fontenot parent = of_find_node_by_path("/"); 252ab519a01SNathan Fontenot } else { 253ab519a01SNathan Fontenot char *parent_path; 254ab519a01SNathan Fontenot int parent_path_len = last_slash - path + 1; 255ab519a01SNathan Fontenot parent_path = kmalloc(parent_path_len, GFP_KERNEL); 256ab519a01SNathan Fontenot if (!parent_path) 257ab519a01SNathan Fontenot return NULL; 258ab519a01SNathan Fontenot 259ab519a01SNathan Fontenot strlcpy(parent_path, path, parent_path_len); 260ab519a01SNathan Fontenot parent = of_find_node_by_path(parent_path); 261ab519a01SNathan Fontenot kfree(parent_path); 262ab519a01SNathan Fontenot } 263ab519a01SNathan Fontenot 264ab519a01SNathan Fontenot return parent; 265ab519a01SNathan Fontenot } 266ab519a01SNathan Fontenot 267ab519a01SNathan Fontenot int dlpar_attach_node(struct device_node *dn) 268ab519a01SNathan Fontenot { 269ab519a01SNathan Fontenot int rc; 270ab519a01SNathan Fontenot 271ab519a01SNathan Fontenot dn->parent = derive_parent(dn->full_name); 272ab519a01SNathan Fontenot if (!dn->parent) 273ab519a01SNathan Fontenot return -ENOMEM; 274ab519a01SNathan Fontenot 2751cf3d8b3SNathan Fontenot rc = of_attach_node(dn); 2763aef19f0SAkinobu Mita if (rc) { 277ab519a01SNathan Fontenot printk(KERN_ERR "Failed to add device node %s\n", 278ab519a01SNathan Fontenot dn->full_name); 2793aef19f0SAkinobu Mita return rc; 280ab519a01SNathan Fontenot } 281ab519a01SNathan Fontenot 282ab519a01SNathan Fontenot of_node_put(dn->parent); 283ab519a01SNathan Fontenot return 0; 284ab519a01SNathan Fontenot } 285ab519a01SNathan Fontenot 286ab519a01SNathan Fontenot int dlpar_detach_node(struct device_node *dn) 287ab519a01SNathan Fontenot { 2885935ff43STyrel Datwyler struct device_node *child; 2891cf3d8b3SNathan Fontenot int rc; 290ab519a01SNathan Fontenot 2915935ff43STyrel Datwyler child = of_get_next_child(dn, NULL); 2925935ff43STyrel Datwyler while (child) { 2935935ff43STyrel Datwyler dlpar_detach_node(child); 2945935ff43STyrel Datwyler child = of_get_next_child(dn, child); 2955935ff43STyrel Datwyler } 2965935ff43STyrel Datwyler 2971cf3d8b3SNathan Fontenot rc = of_detach_node(dn); 2981cf3d8b3SNathan Fontenot if (rc) 2991cf3d8b3SNathan Fontenot return rc; 3001cf3d8b3SNathan Fontenot 3011cf3d8b3SNathan Fontenot of_node_put(dn); /* Must decrement the refcount */ 302ab519a01SNathan Fontenot return 0; 303ab519a01SNathan Fontenot } 304ab519a01SNathan Fontenot 305ab519a01SNathan Fontenot #define DR_ENTITY_SENSE 9003 306ab519a01SNathan Fontenot #define DR_ENTITY_PRESENT 1 307ab519a01SNathan Fontenot #define DR_ENTITY_UNUSABLE 2 308ab519a01SNathan Fontenot #define ALLOCATION_STATE 9003 309ab519a01SNathan Fontenot #define ALLOC_UNUSABLE 0 310ab519a01SNathan Fontenot #define ALLOC_USABLE 1 311ab519a01SNathan Fontenot #define ISOLATION_STATE 9001 312ab519a01SNathan Fontenot #define ISOLATE 0 313ab519a01SNathan Fontenot #define UNISOLATE 1 314ab519a01SNathan Fontenot 315ab519a01SNathan Fontenot int dlpar_acquire_drc(u32 drc_index) 316ab519a01SNathan Fontenot { 317ab519a01SNathan Fontenot int dr_status, rc; 318ab519a01SNathan Fontenot 319ab519a01SNathan Fontenot rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status, 320ab519a01SNathan Fontenot DR_ENTITY_SENSE, drc_index); 321ab519a01SNathan Fontenot if (rc || dr_status != DR_ENTITY_UNUSABLE) 322ab519a01SNathan Fontenot return -1; 323ab519a01SNathan Fontenot 324ab519a01SNathan Fontenot rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE); 325ab519a01SNathan Fontenot if (rc) 326ab519a01SNathan Fontenot return rc; 327ab519a01SNathan Fontenot 328ab519a01SNathan Fontenot rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 329ab519a01SNathan Fontenot if (rc) { 330ab519a01SNathan Fontenot rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 331ab519a01SNathan Fontenot return rc; 332ab519a01SNathan Fontenot } 333ab519a01SNathan Fontenot 334ab519a01SNathan Fontenot return 0; 335ab519a01SNathan Fontenot } 336ab519a01SNathan Fontenot 337ab519a01SNathan Fontenot int dlpar_release_drc(u32 drc_index) 338ab519a01SNathan Fontenot { 339ab519a01SNathan Fontenot int dr_status, rc; 340ab519a01SNathan Fontenot 341ab519a01SNathan Fontenot rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status, 342ab519a01SNathan Fontenot DR_ENTITY_SENSE, drc_index); 343ab519a01SNathan Fontenot if (rc || dr_status != DR_ENTITY_PRESENT) 344ab519a01SNathan Fontenot return -1; 345ab519a01SNathan Fontenot 346ab519a01SNathan Fontenot rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE); 347ab519a01SNathan Fontenot if (rc) 348ab519a01SNathan Fontenot return rc; 349ab519a01SNathan Fontenot 350ab519a01SNathan Fontenot rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 351ab519a01SNathan Fontenot if (rc) { 352ab519a01SNathan Fontenot rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 353ab519a01SNathan Fontenot return rc; 354ab519a01SNathan Fontenot } 355ab519a01SNathan Fontenot 356ab519a01SNathan Fontenot return 0; 357ab519a01SNathan Fontenot } 358ab519a01SNathan Fontenot 3591a8061c4SNathan Fontenot #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE 360ab519a01SNathan Fontenot 361275a64f6SNathan Fontenot static int dlpar_online_cpu(struct device_node *dn) 362275a64f6SNathan Fontenot { 363275a64f6SNathan Fontenot int rc = 0; 364275a64f6SNathan Fontenot unsigned int cpu; 365275a64f6SNathan Fontenot int len, nthreads, i; 366275a64f6SNathan Fontenot const u32 *intserv; 367275a64f6SNathan Fontenot 368275a64f6SNathan Fontenot intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len); 369275a64f6SNathan Fontenot if (!intserv) 370275a64f6SNathan Fontenot return -EINVAL; 371275a64f6SNathan Fontenot 372275a64f6SNathan Fontenot nthreads = len / sizeof(u32); 373275a64f6SNathan Fontenot 374275a64f6SNathan Fontenot cpu_maps_update_begin(); 375275a64f6SNathan Fontenot for (i = 0; i < nthreads; i++) { 376275a64f6SNathan Fontenot for_each_present_cpu(cpu) { 377275a64f6SNathan Fontenot if (get_hard_smp_processor_id(cpu) != intserv[i]) 378275a64f6SNathan Fontenot continue; 379275a64f6SNathan Fontenot BUG_ON(get_cpu_current_state(cpu) 380275a64f6SNathan Fontenot != CPU_STATE_OFFLINE); 381275a64f6SNathan Fontenot cpu_maps_update_done(); 382275a64f6SNathan Fontenot rc = cpu_up(cpu); 383275a64f6SNathan Fontenot if (rc) 384275a64f6SNathan Fontenot goto out; 385275a64f6SNathan Fontenot cpu_maps_update_begin(); 386275a64f6SNathan Fontenot 387275a64f6SNathan Fontenot break; 388275a64f6SNathan Fontenot } 389275a64f6SNathan Fontenot if (cpu == num_possible_cpus()) 390275a64f6SNathan Fontenot printk(KERN_WARNING "Could not find cpu to online " 391275a64f6SNathan Fontenot "with physical id 0x%x\n", intserv[i]); 392275a64f6SNathan Fontenot } 393275a64f6SNathan Fontenot cpu_maps_update_done(); 394275a64f6SNathan Fontenot 395275a64f6SNathan Fontenot out: 396275a64f6SNathan Fontenot return rc; 397275a64f6SNathan Fontenot 398275a64f6SNathan Fontenot } 399275a64f6SNathan Fontenot 4001a8061c4SNathan Fontenot static ssize_t dlpar_cpu_probe(const char *buf, size_t count) 4011a8061c4SNathan Fontenot { 4028d5ff320STyrel Datwyler struct device_node *dn, *parent; 4031a8061c4SNathan Fontenot unsigned long drc_index; 4041a8061c4SNathan Fontenot int rc; 4051a8061c4SNathan Fontenot 4061a8061c4SNathan Fontenot rc = strict_strtoul(buf, 0, &drc_index); 4076dedcca6SToshi Kani if (rc) 4086dedcca6SToshi Kani return -EINVAL; 4091a8061c4SNathan Fontenot 4108d5ff320STyrel Datwyler parent = of_find_node_by_path("/cpus"); 4116dedcca6SToshi Kani if (!parent) 4126dedcca6SToshi Kani return -ENODEV; 4138d5ff320STyrel Datwyler 4148d5ff320STyrel Datwyler dn = dlpar_configure_connector(drc_index, parent); 4156dedcca6SToshi Kani if (!dn) 4166dedcca6SToshi Kani return -EINVAL; 4171a8061c4SNathan Fontenot 4188d5ff320STyrel Datwyler of_node_put(parent); 4191a8061c4SNathan Fontenot 4201a8061c4SNathan Fontenot rc = dlpar_acquire_drc(drc_index); 4211a8061c4SNathan Fontenot if (rc) { 4221a8061c4SNathan Fontenot dlpar_free_cc_nodes(dn); 4236dedcca6SToshi Kani return -EINVAL; 4241a8061c4SNathan Fontenot } 4251a8061c4SNathan Fontenot 4261a8061c4SNathan Fontenot rc = dlpar_attach_node(dn); 4271a8061c4SNathan Fontenot if (rc) { 4281a8061c4SNathan Fontenot dlpar_release_drc(drc_index); 4291a8061c4SNathan Fontenot dlpar_free_cc_nodes(dn); 4306dedcca6SToshi Kani return rc; 4311a8061c4SNathan Fontenot } 4321a8061c4SNathan Fontenot 433275a64f6SNathan Fontenot rc = dlpar_online_cpu(dn); 4346dedcca6SToshi Kani if (rc) 4356dedcca6SToshi Kani return rc; 436b6db63d1SGautham R Shenoy 4376dedcca6SToshi Kani return count; 4381a8061c4SNathan Fontenot } 4391a8061c4SNathan Fontenot 440275a64f6SNathan Fontenot static int dlpar_offline_cpu(struct device_node *dn) 441275a64f6SNathan Fontenot { 442275a64f6SNathan Fontenot int rc = 0; 443275a64f6SNathan Fontenot unsigned int cpu; 444275a64f6SNathan Fontenot int len, nthreads, i; 445275a64f6SNathan Fontenot const u32 *intserv; 446275a64f6SNathan Fontenot 447275a64f6SNathan Fontenot intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len); 448275a64f6SNathan Fontenot if (!intserv) 449275a64f6SNathan Fontenot return -EINVAL; 450275a64f6SNathan Fontenot 451275a64f6SNathan Fontenot nthreads = len / sizeof(u32); 452275a64f6SNathan Fontenot 453275a64f6SNathan Fontenot cpu_maps_update_begin(); 454275a64f6SNathan Fontenot for (i = 0; i < nthreads; i++) { 455275a64f6SNathan Fontenot for_each_present_cpu(cpu) { 456275a64f6SNathan Fontenot if (get_hard_smp_processor_id(cpu) != intserv[i]) 457275a64f6SNathan Fontenot continue; 458275a64f6SNathan Fontenot 459275a64f6SNathan Fontenot if (get_cpu_current_state(cpu) == CPU_STATE_OFFLINE) 460275a64f6SNathan Fontenot break; 461275a64f6SNathan Fontenot 462275a64f6SNathan Fontenot if (get_cpu_current_state(cpu) == CPU_STATE_ONLINE) { 463ceddee23SRobert Jennings set_preferred_offline_state(cpu, CPU_STATE_OFFLINE); 464275a64f6SNathan Fontenot cpu_maps_update_done(); 465275a64f6SNathan Fontenot rc = cpu_down(cpu); 466275a64f6SNathan Fontenot if (rc) 467275a64f6SNathan Fontenot goto out; 468275a64f6SNathan Fontenot cpu_maps_update_begin(); 469275a64f6SNathan Fontenot break; 470275a64f6SNathan Fontenot 471275a64f6SNathan Fontenot } 472275a64f6SNathan Fontenot 473275a64f6SNathan Fontenot /* 474275a64f6SNathan Fontenot * The cpu is in CPU_STATE_INACTIVE. 475275a64f6SNathan Fontenot * Upgrade it's state to CPU_STATE_OFFLINE. 476275a64f6SNathan Fontenot */ 477275a64f6SNathan Fontenot set_preferred_offline_state(cpu, CPU_STATE_OFFLINE); 478275a64f6SNathan Fontenot BUG_ON(plpar_hcall_norets(H_PROD, intserv[i]) 479275a64f6SNathan Fontenot != H_SUCCESS); 480275a64f6SNathan Fontenot __cpu_die(cpu); 481275a64f6SNathan Fontenot break; 482275a64f6SNathan Fontenot } 483275a64f6SNathan Fontenot if (cpu == num_possible_cpus()) 484275a64f6SNathan Fontenot printk(KERN_WARNING "Could not find cpu to offline " 485275a64f6SNathan Fontenot "with physical id 0x%x\n", intserv[i]); 486275a64f6SNathan Fontenot } 487275a64f6SNathan Fontenot cpu_maps_update_done(); 488275a64f6SNathan Fontenot 489275a64f6SNathan Fontenot out: 490275a64f6SNathan Fontenot return rc; 491275a64f6SNathan Fontenot 492275a64f6SNathan Fontenot } 493275a64f6SNathan Fontenot 4941a8061c4SNathan Fontenot static ssize_t dlpar_cpu_release(const char *buf, size_t count) 4951a8061c4SNathan Fontenot { 4961a8061c4SNathan Fontenot struct device_node *dn; 4971a8061c4SNathan Fontenot const u32 *drc_index; 4981a8061c4SNathan Fontenot int rc; 4991a8061c4SNathan Fontenot 5001a8061c4SNathan Fontenot dn = of_find_node_by_path(buf); 5011a8061c4SNathan Fontenot if (!dn) 5021a8061c4SNathan Fontenot return -EINVAL; 5031a8061c4SNathan Fontenot 5041a8061c4SNathan Fontenot drc_index = of_get_property(dn, "ibm,my-drc-index", NULL); 5051a8061c4SNathan Fontenot if (!drc_index) { 5061a8061c4SNathan Fontenot of_node_put(dn); 5071a8061c4SNathan Fontenot return -EINVAL; 5081a8061c4SNathan Fontenot } 5091a8061c4SNathan Fontenot 510275a64f6SNathan Fontenot rc = dlpar_offline_cpu(dn); 511b6db63d1SGautham R Shenoy if (rc) { 512b6db63d1SGautham R Shenoy of_node_put(dn); 5136dedcca6SToshi Kani return -EINVAL; 514b6db63d1SGautham R Shenoy } 515b6db63d1SGautham R Shenoy 5161a8061c4SNathan Fontenot rc = dlpar_release_drc(*drc_index); 5171a8061c4SNathan Fontenot if (rc) { 5181a8061c4SNathan Fontenot of_node_put(dn); 5196dedcca6SToshi Kani return rc; 5201a8061c4SNathan Fontenot } 5211a8061c4SNathan Fontenot 5221a8061c4SNathan Fontenot rc = dlpar_detach_node(dn); 5231a8061c4SNathan Fontenot if (rc) { 5241a8061c4SNathan Fontenot dlpar_acquire_drc(*drc_index); 5256dedcca6SToshi Kani return rc; 5261a8061c4SNathan Fontenot } 5271a8061c4SNathan Fontenot 5281a8061c4SNathan Fontenot of_node_put(dn); 5296dedcca6SToshi Kani 5306dedcca6SToshi Kani return count; 5311a8061c4SNathan Fontenot } 5321a8061c4SNathan Fontenot 5331a8061c4SNathan Fontenot static int __init pseries_dlpar_init(void) 5341a8061c4SNathan Fontenot { 5351a8061c4SNathan Fontenot ppc_md.cpu_probe = dlpar_cpu_probe; 5361a8061c4SNathan Fontenot ppc_md.cpu_release = dlpar_cpu_release; 5371a8061c4SNathan Fontenot 5381a8061c4SNathan Fontenot return 0; 5391a8061c4SNathan Fontenot } 5401a8061c4SNathan Fontenot machine_device_initcall(pseries, pseries_dlpar_init); 5411a8061c4SNathan Fontenot 5421a8061c4SNathan Fontenot #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ 543