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> 195a0e3ad6STejun Heo #include <linux/slab.h> 20b6db63d1SGautham R Shenoy #include "offline_states.h" 21ab519a01SNathan Fontenot 22ab519a01SNathan Fontenot #include <asm/prom.h> 23ab519a01SNathan Fontenot #include <asm/machdep.h> 24ab519a01SNathan Fontenot #include <asm/uaccess.h> 25ab519a01SNathan Fontenot #include <asm/rtas.h> 26ab519a01SNathan Fontenot #include <asm/pSeries_reconfig.h> 27ab519a01SNathan Fontenot 28ab519a01SNathan Fontenot struct cc_workarea { 29ab519a01SNathan Fontenot u32 drc_index; 30ab519a01SNathan Fontenot u32 zero; 31ab519a01SNathan Fontenot u32 name_offset; 32ab519a01SNathan Fontenot u32 prop_length; 33ab519a01SNathan Fontenot u32 prop_offset; 34ab519a01SNathan Fontenot }; 35ab519a01SNathan Fontenot 36ab519a01SNathan Fontenot static void dlpar_free_cc_property(struct property *prop) 37ab519a01SNathan Fontenot { 38ab519a01SNathan Fontenot kfree(prop->name); 39ab519a01SNathan Fontenot kfree(prop->value); 40ab519a01SNathan Fontenot kfree(prop); 41ab519a01SNathan Fontenot } 42ab519a01SNathan Fontenot 43ab519a01SNathan Fontenot static struct property *dlpar_parse_cc_property(struct cc_workarea *ccwa) 44ab519a01SNathan Fontenot { 45ab519a01SNathan Fontenot struct property *prop; 46ab519a01SNathan Fontenot char *name; 47ab519a01SNathan Fontenot char *value; 48ab519a01SNathan Fontenot 49ab519a01SNathan Fontenot prop = kzalloc(sizeof(*prop), GFP_KERNEL); 50ab519a01SNathan Fontenot if (!prop) 51ab519a01SNathan Fontenot return NULL; 52ab519a01SNathan Fontenot 53ab519a01SNathan Fontenot name = (char *)ccwa + ccwa->name_offset; 54ab519a01SNathan Fontenot prop->name = kstrdup(name, GFP_KERNEL); 55ab519a01SNathan Fontenot 56ab519a01SNathan Fontenot prop->length = ccwa->prop_length; 57ab519a01SNathan Fontenot value = (char *)ccwa + ccwa->prop_offset; 58ab519a01SNathan Fontenot prop->value = kzalloc(prop->length, GFP_KERNEL); 59ab519a01SNathan Fontenot if (!prop->value) { 60ab519a01SNathan Fontenot dlpar_free_cc_property(prop); 61ab519a01SNathan Fontenot return NULL; 62ab519a01SNathan Fontenot } 63ab519a01SNathan Fontenot 64ab519a01SNathan Fontenot memcpy(prop->value, value, prop->length); 65ab519a01SNathan Fontenot return prop; 66ab519a01SNathan Fontenot } 67ab519a01SNathan Fontenot 68ab519a01SNathan Fontenot static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa) 69ab519a01SNathan Fontenot { 70ab519a01SNathan Fontenot struct device_node *dn; 71ab519a01SNathan Fontenot char *name; 72ab519a01SNathan Fontenot 73ab519a01SNathan Fontenot dn = kzalloc(sizeof(*dn), GFP_KERNEL); 74ab519a01SNathan Fontenot if (!dn) 75ab519a01SNathan Fontenot return NULL; 76ab519a01SNathan Fontenot 77ab519a01SNathan Fontenot /* The configure connector reported name does not contain a 78ab519a01SNathan Fontenot * preceeding '/', so we allocate a buffer large enough to 79ab519a01SNathan Fontenot * prepend this to the full_name. 80ab519a01SNathan Fontenot */ 81ab519a01SNathan Fontenot name = (char *)ccwa + ccwa->name_offset; 8243caa61fSJulia Lawall dn->full_name = kasprintf(GFP_KERNEL, "/%s", name); 83ab519a01SNathan Fontenot if (!dn->full_name) { 84ab519a01SNathan Fontenot kfree(dn); 85ab519a01SNathan Fontenot return NULL; 86ab519a01SNathan Fontenot } 87ab519a01SNathan Fontenot 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; 132*93f68f1eSNathan Fontenot char *data_buf; 133ab519a01SNathan Fontenot int cc_token; 134*93f68f1eSNathan Fontenot int rc = -1; 135ab519a01SNathan Fontenot 136ab519a01SNathan Fontenot cc_token = rtas_token("ibm,configure-connector"); 137ab519a01SNathan Fontenot if (cc_token == RTAS_UNKNOWN_SERVICE) 138ab519a01SNathan Fontenot return NULL; 139ab519a01SNathan Fontenot 140*93f68f1eSNathan Fontenot data_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL); 141*93f68f1eSNathan Fontenot if (!data_buf) 142*93f68f1eSNathan Fontenot return NULL; 143*93f68f1eSNathan Fontenot 144*93f68f1eSNathan Fontenot ccwa = (struct cc_workarea *)&data_buf[0]; 145ab519a01SNathan Fontenot ccwa->drc_index = drc_index; 146ab519a01SNathan Fontenot ccwa->zero = 0; 147ab519a01SNathan Fontenot 148*93f68f1eSNathan Fontenot do { 149*93f68f1eSNathan Fontenot /* Since we release the rtas_data_buf lock between configure 150*93f68f1eSNathan Fontenot * connector calls we want to re-populate the rtas_data_buffer 151*93f68f1eSNathan Fontenot * with the contents of the previous call. 152*93f68f1eSNathan Fontenot */ 153*93f68f1eSNathan Fontenot spin_lock(&rtas_data_buf_lock); 154*93f68f1eSNathan Fontenot 155*93f68f1eSNathan Fontenot memcpy(rtas_data_buf, data_buf, RTAS_DATA_BUF_SIZE); 156ab519a01SNathan Fontenot rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL); 157*93f68f1eSNathan Fontenot memcpy(data_buf, rtas_data_buf, RTAS_DATA_BUF_SIZE); 158*93f68f1eSNathan Fontenot 159*93f68f1eSNathan Fontenot spin_unlock(&rtas_data_buf_lock); 160*93f68f1eSNathan Fontenot 161ab519a01SNathan Fontenot switch (rc) { 162ab519a01SNathan Fontenot case NEXT_SIBLING: 163ab519a01SNathan Fontenot dn = dlpar_parse_cc_node(ccwa); 164ab519a01SNathan Fontenot if (!dn) 165ab519a01SNathan Fontenot goto cc_error; 166ab519a01SNathan Fontenot 167ab519a01SNathan Fontenot dn->parent = last_dn->parent; 168ab519a01SNathan Fontenot last_dn->sibling = dn; 169ab519a01SNathan Fontenot last_dn = dn; 170ab519a01SNathan Fontenot break; 171ab519a01SNathan Fontenot 172ab519a01SNathan Fontenot case NEXT_CHILD: 173ab519a01SNathan Fontenot dn = dlpar_parse_cc_node(ccwa); 174ab519a01SNathan Fontenot if (!dn) 175ab519a01SNathan Fontenot goto cc_error; 176ab519a01SNathan Fontenot 177ab519a01SNathan Fontenot if (!first_dn) 178ab519a01SNathan Fontenot first_dn = dn; 179ab519a01SNathan Fontenot else { 180ab519a01SNathan Fontenot dn->parent = last_dn; 181ab519a01SNathan Fontenot if (last_dn) 182ab519a01SNathan Fontenot last_dn->child = dn; 183ab519a01SNathan Fontenot } 184ab519a01SNathan Fontenot 185ab519a01SNathan Fontenot last_dn = dn; 186ab519a01SNathan Fontenot break; 187ab519a01SNathan Fontenot 188ab519a01SNathan Fontenot case NEXT_PROPERTY: 189ab519a01SNathan Fontenot property = dlpar_parse_cc_property(ccwa); 190ab519a01SNathan Fontenot if (!property) 191ab519a01SNathan Fontenot goto cc_error; 192ab519a01SNathan Fontenot 193ab519a01SNathan Fontenot if (!last_dn->properties) 194ab519a01SNathan Fontenot last_dn->properties = property; 195ab519a01SNathan Fontenot else 196ab519a01SNathan Fontenot last_property->next = property; 197ab519a01SNathan Fontenot 198ab519a01SNathan Fontenot last_property = property; 199ab519a01SNathan Fontenot break; 200ab519a01SNathan Fontenot 201ab519a01SNathan Fontenot case PREV_PARENT: 202ab519a01SNathan Fontenot last_dn = last_dn->parent; 203ab519a01SNathan Fontenot break; 204ab519a01SNathan Fontenot 205ab519a01SNathan Fontenot case CALL_AGAIN: 206ab519a01SNathan Fontenot break; 207ab519a01SNathan Fontenot 208ab519a01SNathan Fontenot case MORE_MEMORY: 209ab519a01SNathan Fontenot case ERR_CFG_USE: 210ab519a01SNathan Fontenot default: 211ab519a01SNathan Fontenot printk(KERN_ERR "Unexpected Error (%d) " 212ab519a01SNathan Fontenot "returned from configure-connector\n", rc); 213ab519a01SNathan Fontenot goto cc_error; 214ab519a01SNathan Fontenot } 215*93f68f1eSNathan Fontenot } while (rc); 216ab519a01SNathan Fontenot 217ab519a01SNathan Fontenot cc_error: 218*93f68f1eSNathan Fontenot kfree(data_buf); 219*93f68f1eSNathan Fontenot 220*93f68f1eSNathan Fontenot if (rc) { 221ab519a01SNathan Fontenot if (first_dn) 222ab519a01SNathan Fontenot dlpar_free_cc_nodes(first_dn); 223*93f68f1eSNathan Fontenot 224ab519a01SNathan Fontenot return NULL; 225ab519a01SNathan Fontenot } 226ab519a01SNathan Fontenot 227*93f68f1eSNathan Fontenot return first_dn; 228*93f68f1eSNathan Fontenot } 229*93f68f1eSNathan Fontenot 230ab519a01SNathan Fontenot static struct device_node *derive_parent(const char *path) 231ab519a01SNathan Fontenot { 232ab519a01SNathan Fontenot struct device_node *parent; 233ab519a01SNathan Fontenot char *last_slash; 234ab519a01SNathan Fontenot 235ab519a01SNathan Fontenot last_slash = strrchr(path, '/'); 236ab519a01SNathan Fontenot if (last_slash == path) { 237ab519a01SNathan Fontenot parent = of_find_node_by_path("/"); 238ab519a01SNathan Fontenot } else { 239ab519a01SNathan Fontenot char *parent_path; 240ab519a01SNathan Fontenot int parent_path_len = last_slash - path + 1; 241ab519a01SNathan Fontenot parent_path = kmalloc(parent_path_len, GFP_KERNEL); 242ab519a01SNathan Fontenot if (!parent_path) 243ab519a01SNathan Fontenot return NULL; 244ab519a01SNathan Fontenot 245ab519a01SNathan Fontenot strlcpy(parent_path, path, parent_path_len); 246ab519a01SNathan Fontenot parent = of_find_node_by_path(parent_path); 247ab519a01SNathan Fontenot kfree(parent_path); 248ab519a01SNathan Fontenot } 249ab519a01SNathan Fontenot 250ab519a01SNathan Fontenot return parent; 251ab519a01SNathan Fontenot } 252ab519a01SNathan Fontenot 253ab519a01SNathan Fontenot int dlpar_attach_node(struct device_node *dn) 254ab519a01SNathan Fontenot { 25546150a05SFUJITA Tomonori #ifdef CONFIG_PROC_DEVICETREE 256ab519a01SNathan Fontenot struct proc_dir_entry *ent; 25746150a05SFUJITA Tomonori #endif 258ab519a01SNathan Fontenot int rc; 259ab519a01SNathan Fontenot 260ab519a01SNathan Fontenot of_node_set_flag(dn, OF_DYNAMIC); 261ab519a01SNathan Fontenot kref_init(&dn->kref); 262ab519a01SNathan Fontenot dn->parent = derive_parent(dn->full_name); 263ab519a01SNathan Fontenot if (!dn->parent) 264ab519a01SNathan Fontenot return -ENOMEM; 265ab519a01SNathan Fontenot 266ab519a01SNathan Fontenot rc = blocking_notifier_call_chain(&pSeries_reconfig_chain, 267ab519a01SNathan Fontenot PSERIES_RECONFIG_ADD, dn); 268ab519a01SNathan Fontenot if (rc == NOTIFY_BAD) { 269ab519a01SNathan Fontenot printk(KERN_ERR "Failed to add device node %s\n", 270ab519a01SNathan Fontenot dn->full_name); 271ab519a01SNathan Fontenot return -ENOMEM; /* For now, safe to assume kmalloc failure */ 272ab519a01SNathan Fontenot } 273ab519a01SNathan Fontenot 274ab519a01SNathan Fontenot of_attach_node(dn); 275ab519a01SNathan Fontenot 276ab519a01SNathan Fontenot #ifdef CONFIG_PROC_DEVICETREE 277ab519a01SNathan Fontenot ent = proc_mkdir(strrchr(dn->full_name, '/') + 1, dn->parent->pde); 278ab519a01SNathan Fontenot if (ent) 279ab519a01SNathan Fontenot proc_device_tree_add_node(dn, ent); 280ab519a01SNathan Fontenot #endif 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 { 28846150a05SFUJITA Tomonori #ifdef CONFIG_PROC_DEVICETREE 289ab519a01SNathan Fontenot struct device_node *parent = dn->parent; 290ab519a01SNathan Fontenot struct property *prop = dn->properties; 291ab519a01SNathan Fontenot 292ab519a01SNathan Fontenot while (prop) { 293ab519a01SNathan Fontenot remove_proc_entry(prop->name, dn->pde); 294ab519a01SNathan Fontenot prop = prop->next; 295ab519a01SNathan Fontenot } 296ab519a01SNathan Fontenot 297ab519a01SNathan Fontenot if (dn->pde) 298ab519a01SNathan Fontenot remove_proc_entry(dn->pde->name, parent->pde); 299ab519a01SNathan Fontenot #endif 300ab519a01SNathan Fontenot 301ab519a01SNathan Fontenot blocking_notifier_call_chain(&pSeries_reconfig_chain, 302ab519a01SNathan Fontenot PSERIES_RECONFIG_REMOVE, dn); 303ab519a01SNathan Fontenot of_detach_node(dn); 304ab519a01SNathan Fontenot of_node_put(dn); /* Must decrement the refcount */ 305ab519a01SNathan Fontenot 306ab519a01SNathan Fontenot return 0; 307ab519a01SNathan Fontenot } 308ab519a01SNathan Fontenot 309ab519a01SNathan Fontenot #define DR_ENTITY_SENSE 9003 310ab519a01SNathan Fontenot #define DR_ENTITY_PRESENT 1 311ab519a01SNathan Fontenot #define DR_ENTITY_UNUSABLE 2 312ab519a01SNathan Fontenot #define ALLOCATION_STATE 9003 313ab519a01SNathan Fontenot #define ALLOC_UNUSABLE 0 314ab519a01SNathan Fontenot #define ALLOC_USABLE 1 315ab519a01SNathan Fontenot #define ISOLATION_STATE 9001 316ab519a01SNathan Fontenot #define ISOLATE 0 317ab519a01SNathan Fontenot #define UNISOLATE 1 318ab519a01SNathan Fontenot 319ab519a01SNathan Fontenot int dlpar_acquire_drc(u32 drc_index) 320ab519a01SNathan Fontenot { 321ab519a01SNathan Fontenot int dr_status, rc; 322ab519a01SNathan Fontenot 323ab519a01SNathan Fontenot rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status, 324ab519a01SNathan Fontenot DR_ENTITY_SENSE, drc_index); 325ab519a01SNathan Fontenot if (rc || dr_status != DR_ENTITY_UNUSABLE) 326ab519a01SNathan Fontenot return -1; 327ab519a01SNathan Fontenot 328ab519a01SNathan Fontenot rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE); 329ab519a01SNathan Fontenot if (rc) 330ab519a01SNathan Fontenot return rc; 331ab519a01SNathan Fontenot 332ab519a01SNathan Fontenot rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 333ab519a01SNathan Fontenot if (rc) { 334ab519a01SNathan Fontenot rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 335ab519a01SNathan Fontenot return rc; 336ab519a01SNathan Fontenot } 337ab519a01SNathan Fontenot 338ab519a01SNathan Fontenot return 0; 339ab519a01SNathan Fontenot } 340ab519a01SNathan Fontenot 341ab519a01SNathan Fontenot int dlpar_release_drc(u32 drc_index) 342ab519a01SNathan Fontenot { 343ab519a01SNathan Fontenot int dr_status, rc; 344ab519a01SNathan Fontenot 345ab519a01SNathan Fontenot rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status, 346ab519a01SNathan Fontenot DR_ENTITY_SENSE, drc_index); 347ab519a01SNathan Fontenot if (rc || dr_status != DR_ENTITY_PRESENT) 348ab519a01SNathan Fontenot return -1; 349ab519a01SNathan Fontenot 350ab519a01SNathan Fontenot rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE); 351ab519a01SNathan Fontenot if (rc) 352ab519a01SNathan Fontenot return rc; 353ab519a01SNathan Fontenot 354ab519a01SNathan Fontenot rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 355ab519a01SNathan Fontenot if (rc) { 356ab519a01SNathan Fontenot rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 357ab519a01SNathan Fontenot return rc; 358ab519a01SNathan Fontenot } 359ab519a01SNathan Fontenot 360ab519a01SNathan Fontenot return 0; 361ab519a01SNathan Fontenot } 362ab519a01SNathan Fontenot 3631a8061c4SNathan Fontenot #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE 364ab519a01SNathan Fontenot 365275a64f6SNathan Fontenot static int dlpar_online_cpu(struct device_node *dn) 366275a64f6SNathan Fontenot { 367275a64f6SNathan Fontenot int rc = 0; 368275a64f6SNathan Fontenot unsigned int cpu; 369275a64f6SNathan Fontenot int len, nthreads, i; 370275a64f6SNathan Fontenot const u32 *intserv; 371275a64f6SNathan Fontenot 372275a64f6SNathan Fontenot intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len); 373275a64f6SNathan Fontenot if (!intserv) 374275a64f6SNathan Fontenot return -EINVAL; 375275a64f6SNathan Fontenot 376275a64f6SNathan Fontenot nthreads = len / sizeof(u32); 377275a64f6SNathan Fontenot 378275a64f6SNathan Fontenot cpu_maps_update_begin(); 379275a64f6SNathan Fontenot for (i = 0; i < nthreads; i++) { 380275a64f6SNathan Fontenot for_each_present_cpu(cpu) { 381275a64f6SNathan Fontenot if (get_hard_smp_processor_id(cpu) != intserv[i]) 382275a64f6SNathan Fontenot continue; 383275a64f6SNathan Fontenot BUG_ON(get_cpu_current_state(cpu) 384275a64f6SNathan Fontenot != CPU_STATE_OFFLINE); 385275a64f6SNathan Fontenot cpu_maps_update_done(); 386275a64f6SNathan Fontenot rc = cpu_up(cpu); 387275a64f6SNathan Fontenot if (rc) 388275a64f6SNathan Fontenot goto out; 389275a64f6SNathan Fontenot cpu_maps_update_begin(); 390275a64f6SNathan Fontenot 391275a64f6SNathan Fontenot break; 392275a64f6SNathan Fontenot } 393275a64f6SNathan Fontenot if (cpu == num_possible_cpus()) 394275a64f6SNathan Fontenot printk(KERN_WARNING "Could not find cpu to online " 395275a64f6SNathan Fontenot "with physical id 0x%x\n", intserv[i]); 396275a64f6SNathan Fontenot } 397275a64f6SNathan Fontenot cpu_maps_update_done(); 398275a64f6SNathan Fontenot 399275a64f6SNathan Fontenot out: 400275a64f6SNathan Fontenot return rc; 401275a64f6SNathan Fontenot 402275a64f6SNathan Fontenot } 403275a64f6SNathan Fontenot 4041a8061c4SNathan Fontenot static ssize_t dlpar_cpu_probe(const char *buf, size_t count) 4051a8061c4SNathan Fontenot { 4061a8061c4SNathan Fontenot struct device_node *dn; 4071a8061c4SNathan Fontenot unsigned long drc_index; 4081a8061c4SNathan Fontenot char *cpu_name; 4091a8061c4SNathan Fontenot int rc; 4101a8061c4SNathan Fontenot 41151badebdSGautham R Shenoy cpu_hotplug_driver_lock(); 4121a8061c4SNathan Fontenot rc = strict_strtoul(buf, 0, &drc_index); 41351badebdSGautham R Shenoy if (rc) { 41451badebdSGautham R Shenoy rc = -EINVAL; 41551badebdSGautham R Shenoy goto out; 41651badebdSGautham R Shenoy } 4171a8061c4SNathan Fontenot 4181a8061c4SNathan Fontenot dn = dlpar_configure_connector(drc_index); 41951badebdSGautham R Shenoy if (!dn) { 42051badebdSGautham R Shenoy rc = -EINVAL; 42151badebdSGautham R Shenoy goto out; 42251badebdSGautham R Shenoy } 4231a8061c4SNathan Fontenot 4241a8061c4SNathan Fontenot /* configure-connector reports cpus as living in the base 4251a8061c4SNathan Fontenot * directory of the device tree. CPUs actually live in the 4261a8061c4SNathan Fontenot * cpus directory so we need to fixup the full_name. 4271a8061c4SNathan Fontenot */ 42843caa61fSJulia Lawall cpu_name = kasprintf(GFP_KERNEL, "/cpus%s", dn->full_name); 4291a8061c4SNathan Fontenot if (!cpu_name) { 4301a8061c4SNathan Fontenot dlpar_free_cc_nodes(dn); 43151badebdSGautham R Shenoy rc = -ENOMEM; 43251badebdSGautham R Shenoy goto out; 4331a8061c4SNathan Fontenot } 4341a8061c4SNathan Fontenot 4351a8061c4SNathan Fontenot kfree(dn->full_name); 4361a8061c4SNathan Fontenot dn->full_name = cpu_name; 4371a8061c4SNathan Fontenot 4381a8061c4SNathan Fontenot rc = dlpar_acquire_drc(drc_index); 4391a8061c4SNathan Fontenot if (rc) { 4401a8061c4SNathan Fontenot dlpar_free_cc_nodes(dn); 44151badebdSGautham R Shenoy rc = -EINVAL; 44251badebdSGautham R Shenoy goto out; 4431a8061c4SNathan Fontenot } 4441a8061c4SNathan Fontenot 4451a8061c4SNathan Fontenot rc = dlpar_attach_node(dn); 4461a8061c4SNathan Fontenot if (rc) { 4471a8061c4SNathan Fontenot dlpar_release_drc(drc_index); 4481a8061c4SNathan Fontenot dlpar_free_cc_nodes(dn); 449a7df5c5eSJulia Lawall goto out; 4501a8061c4SNathan Fontenot } 4511a8061c4SNathan Fontenot 452275a64f6SNathan Fontenot rc = dlpar_online_cpu(dn); 45351badebdSGautham R Shenoy out: 45451badebdSGautham R Shenoy cpu_hotplug_driver_unlock(); 455b6db63d1SGautham R Shenoy 4561a8061c4SNathan Fontenot return rc ? rc : count; 4571a8061c4SNathan Fontenot } 4581a8061c4SNathan Fontenot 459275a64f6SNathan Fontenot static int dlpar_offline_cpu(struct device_node *dn) 460275a64f6SNathan Fontenot { 461275a64f6SNathan Fontenot int rc = 0; 462275a64f6SNathan Fontenot unsigned int cpu; 463275a64f6SNathan Fontenot int len, nthreads, i; 464275a64f6SNathan Fontenot const u32 *intserv; 465275a64f6SNathan Fontenot 466275a64f6SNathan Fontenot intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len); 467275a64f6SNathan Fontenot if (!intserv) 468275a64f6SNathan Fontenot return -EINVAL; 469275a64f6SNathan Fontenot 470275a64f6SNathan Fontenot nthreads = len / sizeof(u32); 471275a64f6SNathan Fontenot 472275a64f6SNathan Fontenot cpu_maps_update_begin(); 473275a64f6SNathan Fontenot for (i = 0; i < nthreads; i++) { 474275a64f6SNathan Fontenot for_each_present_cpu(cpu) { 475275a64f6SNathan Fontenot if (get_hard_smp_processor_id(cpu) != intserv[i]) 476275a64f6SNathan Fontenot continue; 477275a64f6SNathan Fontenot 478275a64f6SNathan Fontenot if (get_cpu_current_state(cpu) == CPU_STATE_OFFLINE) 479275a64f6SNathan Fontenot break; 480275a64f6SNathan Fontenot 481275a64f6SNathan Fontenot if (get_cpu_current_state(cpu) == CPU_STATE_ONLINE) { 482ceddee23SRobert Jennings set_preferred_offline_state(cpu, CPU_STATE_OFFLINE); 483275a64f6SNathan Fontenot cpu_maps_update_done(); 484275a64f6SNathan Fontenot rc = cpu_down(cpu); 485275a64f6SNathan Fontenot if (rc) 486275a64f6SNathan Fontenot goto out; 487275a64f6SNathan Fontenot cpu_maps_update_begin(); 488275a64f6SNathan Fontenot break; 489275a64f6SNathan Fontenot 490275a64f6SNathan Fontenot } 491275a64f6SNathan Fontenot 492275a64f6SNathan Fontenot /* 493275a64f6SNathan Fontenot * The cpu is in CPU_STATE_INACTIVE. 494275a64f6SNathan Fontenot * Upgrade it's state to CPU_STATE_OFFLINE. 495275a64f6SNathan Fontenot */ 496275a64f6SNathan Fontenot set_preferred_offline_state(cpu, CPU_STATE_OFFLINE); 497275a64f6SNathan Fontenot BUG_ON(plpar_hcall_norets(H_PROD, intserv[i]) 498275a64f6SNathan Fontenot != H_SUCCESS); 499275a64f6SNathan Fontenot __cpu_die(cpu); 500275a64f6SNathan Fontenot break; 501275a64f6SNathan Fontenot } 502275a64f6SNathan Fontenot if (cpu == num_possible_cpus()) 503275a64f6SNathan Fontenot printk(KERN_WARNING "Could not find cpu to offline " 504275a64f6SNathan Fontenot "with physical id 0x%x\n", intserv[i]); 505275a64f6SNathan Fontenot } 506275a64f6SNathan Fontenot cpu_maps_update_done(); 507275a64f6SNathan Fontenot 508275a64f6SNathan Fontenot out: 509275a64f6SNathan Fontenot return rc; 510275a64f6SNathan Fontenot 511275a64f6SNathan Fontenot } 512275a64f6SNathan Fontenot 5131a8061c4SNathan Fontenot static ssize_t dlpar_cpu_release(const char *buf, size_t count) 5141a8061c4SNathan Fontenot { 5151a8061c4SNathan Fontenot struct device_node *dn; 5161a8061c4SNathan Fontenot const u32 *drc_index; 5171a8061c4SNathan Fontenot int rc; 5181a8061c4SNathan Fontenot 5191a8061c4SNathan Fontenot dn = of_find_node_by_path(buf); 5201a8061c4SNathan Fontenot if (!dn) 5211a8061c4SNathan Fontenot return -EINVAL; 5221a8061c4SNathan Fontenot 5231a8061c4SNathan Fontenot drc_index = of_get_property(dn, "ibm,my-drc-index", NULL); 5241a8061c4SNathan Fontenot if (!drc_index) { 5251a8061c4SNathan Fontenot of_node_put(dn); 5261a8061c4SNathan Fontenot return -EINVAL; 5271a8061c4SNathan Fontenot } 5281a8061c4SNathan Fontenot 52951badebdSGautham R Shenoy cpu_hotplug_driver_lock(); 530275a64f6SNathan Fontenot rc = dlpar_offline_cpu(dn); 531b6db63d1SGautham R Shenoy if (rc) { 532b6db63d1SGautham R Shenoy of_node_put(dn); 53351badebdSGautham R Shenoy rc = -EINVAL; 53451badebdSGautham R Shenoy goto out; 535b6db63d1SGautham R Shenoy } 536b6db63d1SGautham R Shenoy 5371a8061c4SNathan Fontenot rc = dlpar_release_drc(*drc_index); 5381a8061c4SNathan Fontenot if (rc) { 5391a8061c4SNathan Fontenot of_node_put(dn); 54051badebdSGautham R Shenoy goto out; 5411a8061c4SNathan Fontenot } 5421a8061c4SNathan Fontenot 5431a8061c4SNathan Fontenot rc = dlpar_detach_node(dn); 5441a8061c4SNathan Fontenot if (rc) { 5451a8061c4SNathan Fontenot dlpar_acquire_drc(*drc_index); 54651badebdSGautham R Shenoy goto out; 5471a8061c4SNathan Fontenot } 5481a8061c4SNathan Fontenot 5491a8061c4SNathan Fontenot of_node_put(dn); 55051badebdSGautham R Shenoy out: 55151badebdSGautham R Shenoy cpu_hotplug_driver_unlock(); 55251badebdSGautham R Shenoy return rc ? rc : count; 5531a8061c4SNathan Fontenot } 5541a8061c4SNathan Fontenot 5551a8061c4SNathan Fontenot static int __init pseries_dlpar_init(void) 5561a8061c4SNathan Fontenot { 5571a8061c4SNathan Fontenot ppc_md.cpu_probe = dlpar_cpu_probe; 5581a8061c4SNathan Fontenot ppc_md.cpu_release = dlpar_cpu_release; 5591a8061c4SNathan Fontenot 5601a8061c4SNathan Fontenot return 0; 5611a8061c4SNathan Fontenot } 5621a8061c4SNathan Fontenot machine_device_initcall(pseries, pseries_dlpar_init); 5631a8061c4SNathan Fontenot 5641a8061c4SNathan Fontenot #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ 565