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> 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 #include <asm/pSeries_reconfig.h> 26ab519a01SNathan Fontenot 27ab519a01SNathan Fontenot struct cc_workarea { 28ab519a01SNathan Fontenot u32 drc_index; 29ab519a01SNathan Fontenot u32 zero; 30ab519a01SNathan Fontenot u32 name_offset; 31ab519a01SNathan Fontenot u32 prop_length; 32ab519a01SNathan Fontenot u32 prop_offset; 33ab519a01SNathan Fontenot }; 34ab519a01SNathan Fontenot 35ab519a01SNathan Fontenot static void dlpar_free_cc_property(struct property *prop) 36ab519a01SNathan Fontenot { 37ab519a01SNathan Fontenot kfree(prop->name); 38ab519a01SNathan Fontenot kfree(prop->value); 39ab519a01SNathan Fontenot kfree(prop); 40ab519a01SNathan Fontenot } 41ab519a01SNathan Fontenot 42ab519a01SNathan Fontenot static struct property *dlpar_parse_cc_property(struct cc_workarea *ccwa) 43ab519a01SNathan Fontenot { 44ab519a01SNathan Fontenot struct property *prop; 45ab519a01SNathan Fontenot char *name; 46ab519a01SNathan Fontenot char *value; 47ab519a01SNathan Fontenot 48ab519a01SNathan Fontenot prop = kzalloc(sizeof(*prop), GFP_KERNEL); 49ab519a01SNathan Fontenot if (!prop) 50ab519a01SNathan Fontenot return NULL; 51ab519a01SNathan Fontenot 52ab519a01SNathan Fontenot name = (char *)ccwa + ccwa->name_offset; 53ab519a01SNathan Fontenot prop->name = kstrdup(name, GFP_KERNEL); 54ab519a01SNathan Fontenot 55ab519a01SNathan Fontenot prop->length = ccwa->prop_length; 56ab519a01SNathan Fontenot value = (char *)ccwa + ccwa->prop_offset; 57ab519a01SNathan Fontenot prop->value = kzalloc(prop->length, GFP_KERNEL); 58ab519a01SNathan Fontenot if (!prop->value) { 59ab519a01SNathan Fontenot dlpar_free_cc_property(prop); 60ab519a01SNathan Fontenot return NULL; 61ab519a01SNathan Fontenot } 62ab519a01SNathan Fontenot 63ab519a01SNathan Fontenot memcpy(prop->value, value, prop->length); 64ab519a01SNathan Fontenot return prop; 65ab519a01SNathan Fontenot } 66ab519a01SNathan Fontenot 67ab519a01SNathan Fontenot static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa) 68ab519a01SNathan Fontenot { 69ab519a01SNathan Fontenot struct device_node *dn; 70ab519a01SNathan Fontenot char *name; 71ab519a01SNathan Fontenot 72ab519a01SNathan Fontenot dn = kzalloc(sizeof(*dn), GFP_KERNEL); 73ab519a01SNathan Fontenot if (!dn) 74ab519a01SNathan Fontenot return NULL; 75ab519a01SNathan Fontenot 76ab519a01SNathan Fontenot /* The configure connector reported name does not contain a 77ab519a01SNathan Fontenot * preceeding '/', so we allocate a buffer large enough to 78ab519a01SNathan Fontenot * prepend this to the full_name. 79ab519a01SNathan Fontenot */ 80ab519a01SNathan Fontenot name = (char *)ccwa + ccwa->name_offset; 81ab519a01SNathan Fontenot dn->full_name = kmalloc(strlen(name) + 2, GFP_KERNEL); 82ab519a01SNathan Fontenot if (!dn->full_name) { 83ab519a01SNathan Fontenot kfree(dn); 84ab519a01SNathan Fontenot return NULL; 85ab519a01SNathan Fontenot } 86ab519a01SNathan Fontenot 87ab519a01SNathan Fontenot sprintf(dn->full_name, "/%s", name); 88ab519a01SNathan Fontenot return dn; 89ab519a01SNathan Fontenot } 90ab519a01SNathan Fontenot 91ab519a01SNathan Fontenot static void dlpar_free_one_cc_node(struct device_node *dn) 92ab519a01SNathan Fontenot { 93ab519a01SNathan Fontenot struct property *prop; 94ab519a01SNathan Fontenot 95ab519a01SNathan Fontenot while (dn->properties) { 96ab519a01SNathan Fontenot prop = dn->properties; 97ab519a01SNathan Fontenot dn->properties = prop->next; 98ab519a01SNathan Fontenot dlpar_free_cc_property(prop); 99ab519a01SNathan Fontenot } 100ab519a01SNathan Fontenot 101ab519a01SNathan Fontenot kfree(dn->full_name); 102ab519a01SNathan Fontenot kfree(dn); 103ab519a01SNathan Fontenot } 104ab519a01SNathan Fontenot 105ab519a01SNathan Fontenot static void dlpar_free_cc_nodes(struct device_node *dn) 106ab519a01SNathan Fontenot { 107ab519a01SNathan Fontenot if (dn->child) 108ab519a01SNathan Fontenot dlpar_free_cc_nodes(dn->child); 109ab519a01SNathan Fontenot 110ab519a01SNathan Fontenot if (dn->sibling) 111ab519a01SNathan Fontenot dlpar_free_cc_nodes(dn->sibling); 112ab519a01SNathan Fontenot 113ab519a01SNathan Fontenot dlpar_free_one_cc_node(dn); 114ab519a01SNathan Fontenot } 115ab519a01SNathan Fontenot 116ab519a01SNathan Fontenot #define NEXT_SIBLING 1 117ab519a01SNathan Fontenot #define NEXT_CHILD 2 118ab519a01SNathan Fontenot #define NEXT_PROPERTY 3 119ab519a01SNathan Fontenot #define PREV_PARENT 4 120ab519a01SNathan Fontenot #define MORE_MEMORY 5 121ab519a01SNathan Fontenot #define CALL_AGAIN -2 122ab519a01SNathan Fontenot #define ERR_CFG_USE -9003 123ab519a01SNathan Fontenot 124ab519a01SNathan Fontenot struct device_node *dlpar_configure_connector(u32 drc_index) 125ab519a01SNathan Fontenot { 126ab519a01SNathan Fontenot struct device_node *dn; 127ab519a01SNathan Fontenot struct device_node *first_dn = NULL; 128ab519a01SNathan Fontenot struct device_node *last_dn = NULL; 129ab519a01SNathan Fontenot struct property *property; 130ab519a01SNathan Fontenot struct property *last_property = NULL; 131ab519a01SNathan Fontenot struct cc_workarea *ccwa; 132ab519a01SNathan Fontenot int cc_token; 133ab519a01SNathan Fontenot int rc; 134ab519a01SNathan Fontenot 135ab519a01SNathan Fontenot cc_token = rtas_token("ibm,configure-connector"); 136ab519a01SNathan Fontenot if (cc_token == RTAS_UNKNOWN_SERVICE) 137ab519a01SNathan Fontenot return NULL; 138ab519a01SNathan Fontenot 139ab519a01SNathan Fontenot spin_lock(&rtas_data_buf_lock); 140ab519a01SNathan Fontenot ccwa = (struct cc_workarea *)&rtas_data_buf[0]; 141ab519a01SNathan Fontenot ccwa->drc_index = drc_index; 142ab519a01SNathan Fontenot ccwa->zero = 0; 143ab519a01SNathan Fontenot 144ab519a01SNathan Fontenot rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL); 145ab519a01SNathan Fontenot while (rc) { 146ab519a01SNathan Fontenot switch (rc) { 147ab519a01SNathan Fontenot case NEXT_SIBLING: 148ab519a01SNathan Fontenot dn = dlpar_parse_cc_node(ccwa); 149ab519a01SNathan Fontenot if (!dn) 150ab519a01SNathan Fontenot goto cc_error; 151ab519a01SNathan Fontenot 152ab519a01SNathan Fontenot dn->parent = last_dn->parent; 153ab519a01SNathan Fontenot last_dn->sibling = dn; 154ab519a01SNathan Fontenot last_dn = dn; 155ab519a01SNathan Fontenot break; 156ab519a01SNathan Fontenot 157ab519a01SNathan Fontenot case NEXT_CHILD: 158ab519a01SNathan Fontenot dn = dlpar_parse_cc_node(ccwa); 159ab519a01SNathan Fontenot if (!dn) 160ab519a01SNathan Fontenot goto cc_error; 161ab519a01SNathan Fontenot 162ab519a01SNathan Fontenot if (!first_dn) 163ab519a01SNathan Fontenot first_dn = dn; 164ab519a01SNathan Fontenot else { 165ab519a01SNathan Fontenot dn->parent = last_dn; 166ab519a01SNathan Fontenot if (last_dn) 167ab519a01SNathan Fontenot last_dn->child = dn; 168ab519a01SNathan Fontenot } 169ab519a01SNathan Fontenot 170ab519a01SNathan Fontenot last_dn = dn; 171ab519a01SNathan Fontenot break; 172ab519a01SNathan Fontenot 173ab519a01SNathan Fontenot case NEXT_PROPERTY: 174ab519a01SNathan Fontenot property = dlpar_parse_cc_property(ccwa); 175ab519a01SNathan Fontenot if (!property) 176ab519a01SNathan Fontenot goto cc_error; 177ab519a01SNathan Fontenot 178ab519a01SNathan Fontenot if (!last_dn->properties) 179ab519a01SNathan Fontenot last_dn->properties = property; 180ab519a01SNathan Fontenot else 181ab519a01SNathan Fontenot last_property->next = property; 182ab519a01SNathan Fontenot 183ab519a01SNathan Fontenot last_property = property; 184ab519a01SNathan Fontenot break; 185ab519a01SNathan Fontenot 186ab519a01SNathan Fontenot case PREV_PARENT: 187ab519a01SNathan Fontenot last_dn = last_dn->parent; 188ab519a01SNathan Fontenot break; 189ab519a01SNathan Fontenot 190ab519a01SNathan Fontenot case CALL_AGAIN: 191ab519a01SNathan Fontenot break; 192ab519a01SNathan Fontenot 193ab519a01SNathan Fontenot case MORE_MEMORY: 194ab519a01SNathan Fontenot case ERR_CFG_USE: 195ab519a01SNathan Fontenot default: 196ab519a01SNathan Fontenot printk(KERN_ERR "Unexpected Error (%d) " 197ab519a01SNathan Fontenot "returned from configure-connector\n", rc); 198ab519a01SNathan Fontenot goto cc_error; 199ab519a01SNathan Fontenot } 200ab519a01SNathan Fontenot 201ab519a01SNathan Fontenot rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL); 202ab519a01SNathan Fontenot } 203ab519a01SNathan Fontenot 204ab519a01SNathan Fontenot spin_unlock(&rtas_data_buf_lock); 205ab519a01SNathan Fontenot return first_dn; 206ab519a01SNathan Fontenot 207ab519a01SNathan Fontenot cc_error: 208ab519a01SNathan Fontenot if (first_dn) 209ab519a01SNathan Fontenot dlpar_free_cc_nodes(first_dn); 210ab519a01SNathan Fontenot spin_unlock(&rtas_data_buf_lock); 211ab519a01SNathan Fontenot return NULL; 212ab519a01SNathan Fontenot } 213ab519a01SNathan Fontenot 214ab519a01SNathan Fontenot static struct device_node *derive_parent(const char *path) 215ab519a01SNathan Fontenot { 216ab519a01SNathan Fontenot struct device_node *parent; 217ab519a01SNathan Fontenot char *last_slash; 218ab519a01SNathan Fontenot 219ab519a01SNathan Fontenot last_slash = strrchr(path, '/'); 220ab519a01SNathan Fontenot if (last_slash == path) { 221ab519a01SNathan Fontenot parent = of_find_node_by_path("/"); 222ab519a01SNathan Fontenot } else { 223ab519a01SNathan Fontenot char *parent_path; 224ab519a01SNathan Fontenot int parent_path_len = last_slash - path + 1; 225ab519a01SNathan Fontenot parent_path = kmalloc(parent_path_len, GFP_KERNEL); 226ab519a01SNathan Fontenot if (!parent_path) 227ab519a01SNathan Fontenot return NULL; 228ab519a01SNathan Fontenot 229ab519a01SNathan Fontenot strlcpy(parent_path, path, parent_path_len); 230ab519a01SNathan Fontenot parent = of_find_node_by_path(parent_path); 231ab519a01SNathan Fontenot kfree(parent_path); 232ab519a01SNathan Fontenot } 233ab519a01SNathan Fontenot 234ab519a01SNathan Fontenot return parent; 235ab519a01SNathan Fontenot } 236ab519a01SNathan Fontenot 237ab519a01SNathan Fontenot int dlpar_attach_node(struct device_node *dn) 238ab519a01SNathan Fontenot { 239ab519a01SNathan Fontenot struct proc_dir_entry *ent; 240ab519a01SNathan Fontenot int rc; 241ab519a01SNathan Fontenot 242ab519a01SNathan Fontenot of_node_set_flag(dn, OF_DYNAMIC); 243ab519a01SNathan Fontenot kref_init(&dn->kref); 244ab519a01SNathan Fontenot dn->parent = derive_parent(dn->full_name); 245ab519a01SNathan Fontenot if (!dn->parent) 246ab519a01SNathan Fontenot return -ENOMEM; 247ab519a01SNathan Fontenot 248ab519a01SNathan Fontenot rc = blocking_notifier_call_chain(&pSeries_reconfig_chain, 249ab519a01SNathan Fontenot PSERIES_RECONFIG_ADD, dn); 250ab519a01SNathan Fontenot if (rc == NOTIFY_BAD) { 251ab519a01SNathan Fontenot printk(KERN_ERR "Failed to add device node %s\n", 252ab519a01SNathan Fontenot dn->full_name); 253ab519a01SNathan Fontenot return -ENOMEM; /* For now, safe to assume kmalloc failure */ 254ab519a01SNathan Fontenot } 255ab519a01SNathan Fontenot 256ab519a01SNathan Fontenot of_attach_node(dn); 257ab519a01SNathan Fontenot 258ab519a01SNathan Fontenot #ifdef CONFIG_PROC_DEVICETREE 259ab519a01SNathan Fontenot ent = proc_mkdir(strrchr(dn->full_name, '/') + 1, dn->parent->pde); 260ab519a01SNathan Fontenot if (ent) 261ab519a01SNathan Fontenot proc_device_tree_add_node(dn, ent); 262ab519a01SNathan Fontenot #endif 263ab519a01SNathan Fontenot 264ab519a01SNathan Fontenot of_node_put(dn->parent); 265ab519a01SNathan Fontenot return 0; 266ab519a01SNathan Fontenot } 267ab519a01SNathan Fontenot 268ab519a01SNathan Fontenot int dlpar_detach_node(struct device_node *dn) 269ab519a01SNathan Fontenot { 270ab519a01SNathan Fontenot struct device_node *parent = dn->parent; 271ab519a01SNathan Fontenot struct property *prop = dn->properties; 272ab519a01SNathan Fontenot 273ab519a01SNathan Fontenot #ifdef CONFIG_PROC_DEVICETREE 274ab519a01SNathan Fontenot while (prop) { 275ab519a01SNathan Fontenot remove_proc_entry(prop->name, dn->pde); 276ab519a01SNathan Fontenot prop = prop->next; 277ab519a01SNathan Fontenot } 278ab519a01SNathan Fontenot 279ab519a01SNathan Fontenot if (dn->pde) 280ab519a01SNathan Fontenot remove_proc_entry(dn->pde->name, parent->pde); 281ab519a01SNathan Fontenot #endif 282ab519a01SNathan Fontenot 283ab519a01SNathan Fontenot blocking_notifier_call_chain(&pSeries_reconfig_chain, 284ab519a01SNathan Fontenot PSERIES_RECONFIG_REMOVE, dn); 285ab519a01SNathan Fontenot of_detach_node(dn); 286ab519a01SNathan Fontenot of_node_put(dn); /* Must decrement the refcount */ 287ab519a01SNathan Fontenot 288ab519a01SNathan Fontenot return 0; 289ab519a01SNathan Fontenot } 290ab519a01SNathan Fontenot 291b6db63d1SGautham R Shenoy int online_node_cpus(struct device_node *dn) 292b6db63d1SGautham R Shenoy { 293b6db63d1SGautham R Shenoy int rc = 0; 294b6db63d1SGautham R Shenoy unsigned int cpu; 295b6db63d1SGautham R Shenoy int len, nthreads, i; 296b6db63d1SGautham R Shenoy const u32 *intserv; 297b6db63d1SGautham R Shenoy 298b6db63d1SGautham R Shenoy intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len); 299b6db63d1SGautham R Shenoy if (!intserv) 300b6db63d1SGautham R Shenoy return -EINVAL; 301b6db63d1SGautham R Shenoy 302b6db63d1SGautham R Shenoy nthreads = len / sizeof(u32); 303b6db63d1SGautham R Shenoy 304b6db63d1SGautham R Shenoy cpu_maps_update_begin(); 305b6db63d1SGautham R Shenoy for (i = 0; i < nthreads; i++) { 306b6db63d1SGautham R Shenoy for_each_present_cpu(cpu) { 307b6db63d1SGautham R Shenoy if (get_hard_smp_processor_id(cpu) != intserv[i]) 308b6db63d1SGautham R Shenoy continue; 309b6db63d1SGautham R Shenoy BUG_ON(get_cpu_current_state(cpu) 310b6db63d1SGautham R Shenoy != CPU_STATE_OFFLINE); 311b6db63d1SGautham R Shenoy cpu_maps_update_done(); 312b6db63d1SGautham R Shenoy rc = cpu_up(cpu); 313b6db63d1SGautham R Shenoy if (rc) 314b6db63d1SGautham R Shenoy goto out; 315b6db63d1SGautham R Shenoy cpu_maps_update_begin(); 316b6db63d1SGautham R Shenoy 317b6db63d1SGautham R Shenoy break; 318b6db63d1SGautham R Shenoy } 319b6db63d1SGautham R Shenoy if (cpu == num_possible_cpus()) 320b6db63d1SGautham R Shenoy printk(KERN_WARNING "Could not find cpu to online " 321b6db63d1SGautham R Shenoy "with physical id 0x%x\n", intserv[i]); 322b6db63d1SGautham R Shenoy } 323b6db63d1SGautham R Shenoy cpu_maps_update_done(); 324b6db63d1SGautham R Shenoy 325b6db63d1SGautham R Shenoy out: 326b6db63d1SGautham R Shenoy return rc; 327b6db63d1SGautham R Shenoy 328b6db63d1SGautham R Shenoy } 329b6db63d1SGautham R Shenoy 330b6db63d1SGautham R Shenoy int offline_node_cpus(struct device_node *dn) 331b6db63d1SGautham R Shenoy { 332b6db63d1SGautham R Shenoy int rc = 0; 333b6db63d1SGautham R Shenoy unsigned int cpu; 334b6db63d1SGautham R Shenoy int len, nthreads, i; 335b6db63d1SGautham R Shenoy const u32 *intserv; 336b6db63d1SGautham R Shenoy 337b6db63d1SGautham R Shenoy intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len); 338b6db63d1SGautham R Shenoy if (!intserv) 339b6db63d1SGautham R Shenoy return -EINVAL; 340b6db63d1SGautham R Shenoy 341b6db63d1SGautham R Shenoy nthreads = len / sizeof(u32); 342b6db63d1SGautham R Shenoy 343b6db63d1SGautham R Shenoy cpu_maps_update_begin(); 344b6db63d1SGautham R Shenoy for (i = 0; i < nthreads; i++) { 345b6db63d1SGautham R Shenoy for_each_present_cpu(cpu) { 346b6db63d1SGautham R Shenoy if (get_hard_smp_processor_id(cpu) != intserv[i]) 347b6db63d1SGautham R Shenoy continue; 348b6db63d1SGautham R Shenoy 349b6db63d1SGautham R Shenoy if (get_cpu_current_state(cpu) == CPU_STATE_OFFLINE) 350b6db63d1SGautham R Shenoy break; 351b6db63d1SGautham R Shenoy 352b6db63d1SGautham R Shenoy if (get_cpu_current_state(cpu) == CPU_STATE_ONLINE) { 353b6db63d1SGautham R Shenoy cpu_maps_update_done(); 354b6db63d1SGautham R Shenoy rc = cpu_down(cpu); 355b6db63d1SGautham R Shenoy if (rc) 356b6db63d1SGautham R Shenoy goto out; 357b6db63d1SGautham R Shenoy cpu_maps_update_begin(); 358b6db63d1SGautham R Shenoy break; 359b6db63d1SGautham R Shenoy 360b6db63d1SGautham R Shenoy } 361b6db63d1SGautham R Shenoy 362b6db63d1SGautham R Shenoy /* 363b6db63d1SGautham R Shenoy * The cpu is in CPU_STATE_INACTIVE. 364b6db63d1SGautham R Shenoy * Upgrade it's state to CPU_STATE_OFFLINE. 365b6db63d1SGautham R Shenoy */ 366b6db63d1SGautham R Shenoy set_preferred_offline_state(cpu, CPU_STATE_OFFLINE); 367b6db63d1SGautham R Shenoy BUG_ON(plpar_hcall_norets(H_PROD, intserv[i]) 368b6db63d1SGautham R Shenoy != H_SUCCESS); 369b6db63d1SGautham R Shenoy __cpu_die(cpu); 370b6db63d1SGautham R Shenoy break; 371b6db63d1SGautham R Shenoy } 372b6db63d1SGautham R Shenoy if (cpu == num_possible_cpus()) 373b6db63d1SGautham R Shenoy printk(KERN_WARNING "Could not find cpu to offline " 374b6db63d1SGautham R Shenoy "with physical id 0x%x\n", intserv[i]); 375b6db63d1SGautham R Shenoy } 376b6db63d1SGautham R Shenoy cpu_maps_update_done(); 377b6db63d1SGautham R Shenoy 378b6db63d1SGautham R Shenoy out: 379b6db63d1SGautham R Shenoy return rc; 380b6db63d1SGautham R Shenoy 381b6db63d1SGautham R Shenoy } 382b6db63d1SGautham R Shenoy 383ab519a01SNathan Fontenot #define DR_ENTITY_SENSE 9003 384ab519a01SNathan Fontenot #define DR_ENTITY_PRESENT 1 385ab519a01SNathan Fontenot #define DR_ENTITY_UNUSABLE 2 386ab519a01SNathan Fontenot #define ALLOCATION_STATE 9003 387ab519a01SNathan Fontenot #define ALLOC_UNUSABLE 0 388ab519a01SNathan Fontenot #define ALLOC_USABLE 1 389ab519a01SNathan Fontenot #define ISOLATION_STATE 9001 390ab519a01SNathan Fontenot #define ISOLATE 0 391ab519a01SNathan Fontenot #define UNISOLATE 1 392ab519a01SNathan Fontenot 393ab519a01SNathan Fontenot int dlpar_acquire_drc(u32 drc_index) 394ab519a01SNathan Fontenot { 395ab519a01SNathan Fontenot int dr_status, rc; 396ab519a01SNathan Fontenot 397ab519a01SNathan Fontenot rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status, 398ab519a01SNathan Fontenot DR_ENTITY_SENSE, drc_index); 399ab519a01SNathan Fontenot if (rc || dr_status != DR_ENTITY_UNUSABLE) 400ab519a01SNathan Fontenot return -1; 401ab519a01SNathan Fontenot 402ab519a01SNathan Fontenot rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE); 403ab519a01SNathan Fontenot if (rc) 404ab519a01SNathan Fontenot return rc; 405ab519a01SNathan Fontenot 406ab519a01SNathan Fontenot rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 407ab519a01SNathan Fontenot if (rc) { 408ab519a01SNathan Fontenot rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 409ab519a01SNathan Fontenot return rc; 410ab519a01SNathan Fontenot } 411ab519a01SNathan Fontenot 412ab519a01SNathan Fontenot return 0; 413ab519a01SNathan Fontenot } 414ab519a01SNathan Fontenot 415ab519a01SNathan Fontenot int dlpar_release_drc(u32 drc_index) 416ab519a01SNathan Fontenot { 417ab519a01SNathan Fontenot int dr_status, rc; 418ab519a01SNathan Fontenot 419ab519a01SNathan Fontenot rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status, 420ab519a01SNathan Fontenot DR_ENTITY_SENSE, drc_index); 421ab519a01SNathan Fontenot if (rc || dr_status != DR_ENTITY_PRESENT) 422ab519a01SNathan Fontenot return -1; 423ab519a01SNathan Fontenot 424ab519a01SNathan Fontenot rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE); 425ab519a01SNathan Fontenot if (rc) 426ab519a01SNathan Fontenot return rc; 427ab519a01SNathan Fontenot 428ab519a01SNathan Fontenot rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 429ab519a01SNathan Fontenot if (rc) { 430ab519a01SNathan Fontenot rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 431ab519a01SNathan Fontenot return rc; 432ab519a01SNathan Fontenot } 433ab519a01SNathan Fontenot 434ab519a01SNathan Fontenot return 0; 435ab519a01SNathan Fontenot } 436ab519a01SNathan Fontenot 4371a8061c4SNathan Fontenot #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE 438ab519a01SNathan Fontenot 439*51badebdSGautham R Shenoy static DEFINE_MUTEX(pseries_cpu_hotplug_mutex); 440*51badebdSGautham R Shenoy 441*51badebdSGautham R Shenoy void cpu_hotplug_driver_lock() 442*51badebdSGautham R Shenoy { 443*51badebdSGautham R Shenoy mutex_lock(&pseries_cpu_hotplug_mutex); 444*51badebdSGautham R Shenoy } 445*51badebdSGautham R Shenoy 446*51badebdSGautham R Shenoy void cpu_hotplug_driver_unlock() 447*51badebdSGautham R Shenoy { 448*51badebdSGautham R Shenoy mutex_unlock(&pseries_cpu_hotplug_mutex); 449*51badebdSGautham R Shenoy } 450*51badebdSGautham R Shenoy 4511a8061c4SNathan Fontenot static ssize_t dlpar_cpu_probe(const char *buf, size_t count) 4521a8061c4SNathan Fontenot { 4531a8061c4SNathan Fontenot struct device_node *dn; 4541a8061c4SNathan Fontenot unsigned long drc_index; 4551a8061c4SNathan Fontenot char *cpu_name; 4561a8061c4SNathan Fontenot int rc; 4571a8061c4SNathan Fontenot 458*51badebdSGautham R Shenoy cpu_hotplug_driver_lock(); 4591a8061c4SNathan Fontenot rc = strict_strtoul(buf, 0, &drc_index); 460*51badebdSGautham R Shenoy if (rc) { 461*51badebdSGautham R Shenoy rc = -EINVAL; 462*51badebdSGautham R Shenoy goto out; 463*51badebdSGautham R Shenoy } 4641a8061c4SNathan Fontenot 4651a8061c4SNathan Fontenot dn = dlpar_configure_connector(drc_index); 466*51badebdSGautham R Shenoy if (!dn) { 467*51badebdSGautham R Shenoy rc = -EINVAL; 468*51badebdSGautham R Shenoy goto out; 469*51badebdSGautham R Shenoy } 4701a8061c4SNathan Fontenot 4711a8061c4SNathan Fontenot /* configure-connector reports cpus as living in the base 4721a8061c4SNathan Fontenot * directory of the device tree. CPUs actually live in the 4731a8061c4SNathan Fontenot * cpus directory so we need to fixup the full_name. 4741a8061c4SNathan Fontenot */ 4751a8061c4SNathan Fontenot cpu_name = kzalloc(strlen(dn->full_name) + strlen("/cpus") + 1, 4761a8061c4SNathan Fontenot GFP_KERNEL); 4771a8061c4SNathan Fontenot if (!cpu_name) { 4781a8061c4SNathan Fontenot dlpar_free_cc_nodes(dn); 479*51badebdSGautham R Shenoy rc = -ENOMEM; 480*51badebdSGautham R Shenoy goto out; 4811a8061c4SNathan Fontenot } 4821a8061c4SNathan Fontenot 4831a8061c4SNathan Fontenot sprintf(cpu_name, "/cpus%s", dn->full_name); 4841a8061c4SNathan Fontenot kfree(dn->full_name); 4851a8061c4SNathan Fontenot dn->full_name = cpu_name; 4861a8061c4SNathan Fontenot 4871a8061c4SNathan Fontenot rc = dlpar_acquire_drc(drc_index); 4881a8061c4SNathan Fontenot if (rc) { 4891a8061c4SNathan Fontenot dlpar_free_cc_nodes(dn); 490*51badebdSGautham R Shenoy rc = -EINVAL; 491*51badebdSGautham R Shenoy goto out; 4921a8061c4SNathan Fontenot } 4931a8061c4SNathan Fontenot 4941a8061c4SNathan Fontenot rc = dlpar_attach_node(dn); 4951a8061c4SNathan Fontenot if (rc) { 4961a8061c4SNathan Fontenot dlpar_release_drc(drc_index); 4971a8061c4SNathan Fontenot dlpar_free_cc_nodes(dn); 4981a8061c4SNathan Fontenot } 4991a8061c4SNathan Fontenot 500b6db63d1SGautham R Shenoy rc = online_node_cpus(dn); 501*51badebdSGautham R Shenoy out: 502*51badebdSGautham R Shenoy cpu_hotplug_driver_unlock(); 503b6db63d1SGautham R Shenoy 5041a8061c4SNathan Fontenot return rc ? rc : count; 5051a8061c4SNathan Fontenot } 5061a8061c4SNathan Fontenot 5071a8061c4SNathan Fontenot static ssize_t dlpar_cpu_release(const char *buf, size_t count) 5081a8061c4SNathan Fontenot { 5091a8061c4SNathan Fontenot struct device_node *dn; 5101a8061c4SNathan Fontenot const u32 *drc_index; 5111a8061c4SNathan Fontenot int rc; 5121a8061c4SNathan Fontenot 5131a8061c4SNathan Fontenot dn = of_find_node_by_path(buf); 5141a8061c4SNathan Fontenot if (!dn) 5151a8061c4SNathan Fontenot return -EINVAL; 5161a8061c4SNathan Fontenot 5171a8061c4SNathan Fontenot drc_index = of_get_property(dn, "ibm,my-drc-index", NULL); 5181a8061c4SNathan Fontenot if (!drc_index) { 5191a8061c4SNathan Fontenot of_node_put(dn); 5201a8061c4SNathan Fontenot return -EINVAL; 5211a8061c4SNathan Fontenot } 5221a8061c4SNathan Fontenot 523*51badebdSGautham R Shenoy cpu_hotplug_driver_lock(); 524b6db63d1SGautham R Shenoy rc = offline_node_cpus(dn); 525b6db63d1SGautham R Shenoy if (rc) { 526b6db63d1SGautham R Shenoy of_node_put(dn); 527*51badebdSGautham R Shenoy rc = -EINVAL; 528*51badebdSGautham R Shenoy goto out; 529b6db63d1SGautham R Shenoy } 530b6db63d1SGautham R Shenoy 5311a8061c4SNathan Fontenot rc = dlpar_release_drc(*drc_index); 5321a8061c4SNathan Fontenot if (rc) { 5331a8061c4SNathan Fontenot of_node_put(dn); 534*51badebdSGautham R Shenoy goto out; 5351a8061c4SNathan Fontenot } 5361a8061c4SNathan Fontenot 5371a8061c4SNathan Fontenot rc = dlpar_detach_node(dn); 5381a8061c4SNathan Fontenot if (rc) { 5391a8061c4SNathan Fontenot dlpar_acquire_drc(*drc_index); 540*51badebdSGautham R Shenoy goto out; 5411a8061c4SNathan Fontenot } 5421a8061c4SNathan Fontenot 5431a8061c4SNathan Fontenot of_node_put(dn); 544*51badebdSGautham R Shenoy out: 545*51badebdSGautham R Shenoy cpu_hotplug_driver_unlock(); 546*51badebdSGautham R Shenoy return rc ? rc : count; 5471a8061c4SNathan Fontenot } 5481a8061c4SNathan Fontenot 5491a8061c4SNathan Fontenot static int __init pseries_dlpar_init(void) 5501a8061c4SNathan Fontenot { 5511a8061c4SNathan Fontenot ppc_md.cpu_probe = dlpar_cpu_probe; 5521a8061c4SNathan Fontenot ppc_md.cpu_release = dlpar_cpu_release; 5531a8061c4SNathan Fontenot 5541a8061c4SNathan Fontenot return 0; 5551a8061c4SNathan Fontenot } 5561a8061c4SNathan Fontenot machine_device_initcall(pseries, pseries_dlpar_init); 5571a8061c4SNathan Fontenot 5581a8061c4SNathan Fontenot #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ 559