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 13*999e2dadSNathan Fontenot #define pr_fmt(fmt) "dlpar: " fmt 14*999e2dadSNathan Fontenot 15ab519a01SNathan Fontenot #include <linux/kernel.h> 16ab519a01SNathan Fontenot #include <linux/notifier.h> 17ab519a01SNathan Fontenot #include <linux/spinlock.h> 18ab519a01SNathan Fontenot #include <linux/cpu.h> 195a0e3ad6STejun Heo #include <linux/slab.h> 201cf3d8b3SNathan Fontenot #include <linux/of.h> 21b6db63d1SGautham R Shenoy #include "offline_states.h" 221217d34bSAnton Blanchard #include "pseries.h" 23ab519a01SNathan Fontenot 24ab519a01SNathan Fontenot #include <asm/prom.h> 25ab519a01SNathan Fontenot #include <asm/machdep.h> 26ab519a01SNathan Fontenot #include <asm/uaccess.h> 27ab519a01SNathan Fontenot #include <asm/rtas.h> 28ab519a01SNathan Fontenot 29ab519a01SNathan Fontenot struct cc_workarea { 30d6f1e7abSBharata B Rao __be32 drc_index; 31d6f1e7abSBharata B Rao __be32 zero; 32d6f1e7abSBharata B Rao __be32 name_offset; 33d6f1e7abSBharata B Rao __be32 prop_length; 34d6f1e7abSBharata B Rao __be32 prop_offset; 35ab519a01SNathan Fontenot }; 36ab519a01SNathan Fontenot 3720648974SNathan Fontenot void dlpar_free_cc_property(struct property *prop) 38ab519a01SNathan Fontenot { 39ab519a01SNathan Fontenot kfree(prop->name); 40ab519a01SNathan Fontenot kfree(prop->value); 41ab519a01SNathan Fontenot kfree(prop); 42ab519a01SNathan Fontenot } 43ab519a01SNathan Fontenot 44ab519a01SNathan Fontenot static struct property *dlpar_parse_cc_property(struct cc_workarea *ccwa) 45ab519a01SNathan Fontenot { 46ab519a01SNathan Fontenot struct property *prop; 47ab519a01SNathan Fontenot char *name; 48ab519a01SNathan Fontenot char *value; 49ab519a01SNathan Fontenot 50ab519a01SNathan Fontenot prop = kzalloc(sizeof(*prop), GFP_KERNEL); 51ab519a01SNathan Fontenot if (!prop) 52ab519a01SNathan Fontenot return NULL; 53ab519a01SNathan Fontenot 54d6f1e7abSBharata B Rao name = (char *)ccwa + be32_to_cpu(ccwa->name_offset); 55ab519a01SNathan Fontenot prop->name = kstrdup(name, GFP_KERNEL); 56ab519a01SNathan Fontenot 57d6f1e7abSBharata B Rao prop->length = be32_to_cpu(ccwa->prop_length); 58d6f1e7abSBharata B Rao value = (char *)ccwa + be32_to_cpu(ccwa->prop_offset); 59e72ed6b5SNishanth Aravamudan prop->value = kmemdup(value, prop->length, GFP_KERNEL); 60ab519a01SNathan Fontenot if (!prop->value) { 61ab519a01SNathan Fontenot dlpar_free_cc_property(prop); 62ab519a01SNathan Fontenot return NULL; 63ab519a01SNathan Fontenot } 64ab519a01SNathan Fontenot 65ab519a01SNathan Fontenot return prop; 66ab519a01SNathan Fontenot } 67ab519a01SNathan Fontenot 688d5ff320STyrel Datwyler static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa, 698d5ff320STyrel Datwyler const char *path) 70ab519a01SNathan Fontenot { 71ab519a01SNathan Fontenot struct device_node *dn; 72ab519a01SNathan Fontenot char *name; 73ab519a01SNathan Fontenot 748d5ff320STyrel Datwyler /* If parent node path is "/" advance path to NULL terminator to 758d5ff320STyrel Datwyler * prevent double leading slashs in full_name. 768d5ff320STyrel Datwyler */ 778d5ff320STyrel Datwyler if (!path[1]) 788d5ff320STyrel Datwyler path++; 798d5ff320STyrel Datwyler 80ab519a01SNathan Fontenot dn = kzalloc(sizeof(*dn), GFP_KERNEL); 81ab519a01SNathan Fontenot if (!dn) 82ab519a01SNathan Fontenot return NULL; 83ab519a01SNathan Fontenot 84d6f1e7abSBharata B Rao name = (char *)ccwa + be32_to_cpu(ccwa->name_offset); 858d5ff320STyrel Datwyler dn->full_name = kasprintf(GFP_KERNEL, "%s/%s", path, name); 86ab519a01SNathan Fontenot if (!dn->full_name) { 87ab519a01SNathan Fontenot kfree(dn); 88ab519a01SNathan Fontenot return NULL; 89ab519a01SNathan Fontenot } 90ab519a01SNathan Fontenot 911578cb76STyrel Datwyler of_node_set_flag(dn, OF_DYNAMIC); 9297a9a717STyrel Datwyler of_node_init(dn); 931578cb76STyrel Datwyler 94ab519a01SNathan Fontenot return dn; 95ab519a01SNathan Fontenot } 96ab519a01SNathan Fontenot 97ab519a01SNathan Fontenot static void dlpar_free_one_cc_node(struct device_node *dn) 98ab519a01SNathan Fontenot { 99ab519a01SNathan Fontenot struct property *prop; 100ab519a01SNathan Fontenot 101ab519a01SNathan Fontenot while (dn->properties) { 102ab519a01SNathan Fontenot prop = dn->properties; 103ab519a01SNathan Fontenot dn->properties = prop->next; 104ab519a01SNathan Fontenot dlpar_free_cc_property(prop); 105ab519a01SNathan Fontenot } 106ab519a01SNathan Fontenot 107ab519a01SNathan Fontenot kfree(dn->full_name); 108ab519a01SNathan Fontenot kfree(dn); 109ab519a01SNathan Fontenot } 110ab519a01SNathan Fontenot 11120648974SNathan Fontenot void dlpar_free_cc_nodes(struct device_node *dn) 112ab519a01SNathan Fontenot { 113ab519a01SNathan Fontenot if (dn->child) 114ab519a01SNathan Fontenot dlpar_free_cc_nodes(dn->child); 115ab519a01SNathan Fontenot 116ab519a01SNathan Fontenot if (dn->sibling) 117ab519a01SNathan Fontenot dlpar_free_cc_nodes(dn->sibling); 118ab519a01SNathan Fontenot 119ab519a01SNathan Fontenot dlpar_free_one_cc_node(dn); 120ab519a01SNathan Fontenot } 121ab519a01SNathan Fontenot 1229c740025SAnton Blanchard #define COMPLETE 0 123ab519a01SNathan Fontenot #define NEXT_SIBLING 1 124ab519a01SNathan Fontenot #define NEXT_CHILD 2 125ab519a01SNathan Fontenot #define NEXT_PROPERTY 3 126ab519a01SNathan Fontenot #define PREV_PARENT 4 127ab519a01SNathan Fontenot #define MORE_MEMORY 5 128ab519a01SNathan Fontenot #define CALL_AGAIN -2 129ab519a01SNathan Fontenot #define ERR_CFG_USE -9003 130ab519a01SNathan Fontenot 131d6f1e7abSBharata B Rao struct device_node *dlpar_configure_connector(__be32 drc_index, 1328d5ff320STyrel Datwyler struct device_node *parent) 133ab519a01SNathan Fontenot { 134ab519a01SNathan Fontenot struct device_node *dn; 135ab519a01SNathan Fontenot struct device_node *first_dn = NULL; 136ab519a01SNathan Fontenot struct device_node *last_dn = NULL; 137ab519a01SNathan Fontenot struct property *property; 138ab519a01SNathan Fontenot struct property *last_property = NULL; 139ab519a01SNathan Fontenot struct cc_workarea *ccwa; 14093f68f1eSNathan Fontenot char *data_buf; 1418d5ff320STyrel Datwyler const char *parent_path = parent->full_name; 142ab519a01SNathan Fontenot int cc_token; 14393f68f1eSNathan Fontenot int rc = -1; 144ab519a01SNathan Fontenot 145ab519a01SNathan Fontenot cc_token = rtas_token("ibm,configure-connector"); 146ab519a01SNathan Fontenot if (cc_token == RTAS_UNKNOWN_SERVICE) 147ab519a01SNathan Fontenot return NULL; 148ab519a01SNathan Fontenot 14993f68f1eSNathan Fontenot data_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL); 15093f68f1eSNathan Fontenot if (!data_buf) 15193f68f1eSNathan Fontenot return NULL; 15293f68f1eSNathan Fontenot 15393f68f1eSNathan Fontenot ccwa = (struct cc_workarea *)&data_buf[0]; 154ab519a01SNathan Fontenot ccwa->drc_index = drc_index; 155ab519a01SNathan Fontenot ccwa->zero = 0; 156ab519a01SNathan Fontenot 15793f68f1eSNathan Fontenot do { 15893f68f1eSNathan Fontenot /* Since we release the rtas_data_buf lock between configure 15993f68f1eSNathan Fontenot * connector calls we want to re-populate the rtas_data_buffer 16093f68f1eSNathan Fontenot * with the contents of the previous call. 16193f68f1eSNathan Fontenot */ 16293f68f1eSNathan Fontenot spin_lock(&rtas_data_buf_lock); 16393f68f1eSNathan Fontenot 16493f68f1eSNathan Fontenot memcpy(rtas_data_buf, data_buf, RTAS_DATA_BUF_SIZE); 165ab519a01SNathan Fontenot rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL); 16693f68f1eSNathan Fontenot memcpy(data_buf, rtas_data_buf, RTAS_DATA_BUF_SIZE); 16793f68f1eSNathan Fontenot 16893f68f1eSNathan Fontenot spin_unlock(&rtas_data_buf_lock); 16993f68f1eSNathan Fontenot 170ab519a01SNathan Fontenot switch (rc) { 1719c740025SAnton Blanchard case COMPLETE: 1729c740025SAnton Blanchard break; 1739c740025SAnton Blanchard 174ab519a01SNathan Fontenot case NEXT_SIBLING: 1758d5ff320STyrel Datwyler dn = dlpar_parse_cc_node(ccwa, parent_path); 176ab519a01SNathan Fontenot if (!dn) 177ab519a01SNathan Fontenot goto cc_error; 178ab519a01SNathan Fontenot 179ab519a01SNathan Fontenot dn->parent = last_dn->parent; 180ab519a01SNathan Fontenot last_dn->sibling = dn; 181ab519a01SNathan Fontenot last_dn = dn; 182ab519a01SNathan Fontenot break; 183ab519a01SNathan Fontenot 184ab519a01SNathan Fontenot case NEXT_CHILD: 1858d5ff320STyrel Datwyler if (first_dn) 1868d5ff320STyrel Datwyler parent_path = last_dn->full_name; 1878d5ff320STyrel Datwyler 1888d5ff320STyrel Datwyler dn = dlpar_parse_cc_node(ccwa, parent_path); 189ab519a01SNathan Fontenot if (!dn) 190ab519a01SNathan Fontenot goto cc_error; 191ab519a01SNathan Fontenot 1928d5ff320STyrel Datwyler if (!first_dn) { 1938d5ff320STyrel Datwyler dn->parent = parent; 194ab519a01SNathan Fontenot first_dn = dn; 1958d5ff320STyrel Datwyler } else { 196ab519a01SNathan Fontenot dn->parent = last_dn; 197ab519a01SNathan Fontenot if (last_dn) 198ab519a01SNathan Fontenot last_dn->child = dn; 199ab519a01SNathan Fontenot } 200ab519a01SNathan Fontenot 201ab519a01SNathan Fontenot last_dn = dn; 202ab519a01SNathan Fontenot break; 203ab519a01SNathan Fontenot 204ab519a01SNathan Fontenot case NEXT_PROPERTY: 205ab519a01SNathan Fontenot property = dlpar_parse_cc_property(ccwa); 206ab519a01SNathan Fontenot if (!property) 207ab519a01SNathan Fontenot goto cc_error; 208ab519a01SNathan Fontenot 209ab519a01SNathan Fontenot if (!last_dn->properties) 210ab519a01SNathan Fontenot last_dn->properties = property; 211ab519a01SNathan Fontenot else 212ab519a01SNathan Fontenot last_property->next = property; 213ab519a01SNathan Fontenot 214ab519a01SNathan Fontenot last_property = property; 215ab519a01SNathan Fontenot break; 216ab519a01SNathan Fontenot 217ab519a01SNathan Fontenot case PREV_PARENT: 218ab519a01SNathan Fontenot last_dn = last_dn->parent; 2198d5ff320STyrel Datwyler parent_path = last_dn->parent->full_name; 220ab519a01SNathan Fontenot break; 221ab519a01SNathan Fontenot 222ab519a01SNathan Fontenot case CALL_AGAIN: 223ab519a01SNathan Fontenot break; 224ab519a01SNathan Fontenot 225ab519a01SNathan Fontenot case MORE_MEMORY: 226ab519a01SNathan Fontenot case ERR_CFG_USE: 227ab519a01SNathan Fontenot default: 228ab519a01SNathan Fontenot printk(KERN_ERR "Unexpected Error (%d) " 229ab519a01SNathan Fontenot "returned from configure-connector\n", rc); 230ab519a01SNathan Fontenot goto cc_error; 231ab519a01SNathan Fontenot } 23293f68f1eSNathan Fontenot } while (rc); 233ab519a01SNathan Fontenot 234ab519a01SNathan Fontenot cc_error: 23593f68f1eSNathan Fontenot kfree(data_buf); 23693f68f1eSNathan Fontenot 23793f68f1eSNathan Fontenot if (rc) { 238ab519a01SNathan Fontenot if (first_dn) 239ab519a01SNathan Fontenot dlpar_free_cc_nodes(first_dn); 24093f68f1eSNathan Fontenot 241ab519a01SNathan Fontenot return NULL; 242ab519a01SNathan Fontenot } 243ab519a01SNathan Fontenot 24493f68f1eSNathan Fontenot return first_dn; 24593f68f1eSNathan Fontenot } 24693f68f1eSNathan Fontenot 247ab519a01SNathan Fontenot static struct device_node *derive_parent(const char *path) 248ab519a01SNathan Fontenot { 249ab519a01SNathan Fontenot struct device_node *parent; 250ab519a01SNathan Fontenot char *last_slash; 251ab519a01SNathan Fontenot 252ab519a01SNathan Fontenot last_slash = strrchr(path, '/'); 253ab519a01SNathan Fontenot if (last_slash == path) { 254ab519a01SNathan Fontenot parent = of_find_node_by_path("/"); 255ab519a01SNathan Fontenot } else { 256ab519a01SNathan Fontenot char *parent_path; 257ab519a01SNathan Fontenot int parent_path_len = last_slash - path + 1; 258ab519a01SNathan Fontenot parent_path = kmalloc(parent_path_len, GFP_KERNEL); 259ab519a01SNathan Fontenot if (!parent_path) 260ab519a01SNathan Fontenot return NULL; 261ab519a01SNathan Fontenot 262ab519a01SNathan Fontenot strlcpy(parent_path, path, parent_path_len); 263ab519a01SNathan Fontenot parent = of_find_node_by_path(parent_path); 264ab519a01SNathan Fontenot kfree(parent_path); 265ab519a01SNathan Fontenot } 266ab519a01SNathan Fontenot 267ab519a01SNathan Fontenot return parent; 268ab519a01SNathan Fontenot } 269ab519a01SNathan Fontenot 270ab519a01SNathan Fontenot int dlpar_attach_node(struct device_node *dn) 271ab519a01SNathan Fontenot { 272ab519a01SNathan Fontenot int rc; 273ab519a01SNathan Fontenot 274ab519a01SNathan Fontenot dn->parent = derive_parent(dn->full_name); 275ab519a01SNathan Fontenot if (!dn->parent) 276ab519a01SNathan Fontenot return -ENOMEM; 277ab519a01SNathan Fontenot 2781cf3d8b3SNathan Fontenot rc = of_attach_node(dn); 2793aef19f0SAkinobu Mita if (rc) { 280ab519a01SNathan Fontenot printk(KERN_ERR "Failed to add device node %s\n", 281ab519a01SNathan Fontenot dn->full_name); 2823aef19f0SAkinobu Mita return rc; 283ab519a01SNathan Fontenot } 284ab519a01SNathan Fontenot 285ab519a01SNathan Fontenot of_node_put(dn->parent); 286ab519a01SNathan Fontenot return 0; 287ab519a01SNathan Fontenot } 288ab519a01SNathan Fontenot 289ab519a01SNathan Fontenot int dlpar_detach_node(struct device_node *dn) 290ab519a01SNathan Fontenot { 2915935ff43STyrel Datwyler struct device_node *child; 2921cf3d8b3SNathan Fontenot int rc; 293ab519a01SNathan Fontenot 2945935ff43STyrel Datwyler child = of_get_next_child(dn, NULL); 2955935ff43STyrel Datwyler while (child) { 2965935ff43STyrel Datwyler dlpar_detach_node(child); 2975935ff43STyrel Datwyler child = of_get_next_child(dn, child); 2985935ff43STyrel Datwyler } 2995935ff43STyrel Datwyler 3001cf3d8b3SNathan Fontenot rc = of_detach_node(dn); 3011cf3d8b3SNathan Fontenot if (rc) 3021cf3d8b3SNathan Fontenot return rc; 3031cf3d8b3SNathan Fontenot 3041cf3d8b3SNathan Fontenot of_node_put(dn); /* Must decrement the refcount */ 305ab519a01SNathan Fontenot return 0; 306ab519a01SNathan Fontenot } 307ab519a01SNathan Fontenot 308ab519a01SNathan Fontenot #define DR_ENTITY_SENSE 9003 309ab519a01SNathan Fontenot #define DR_ENTITY_PRESENT 1 310ab519a01SNathan Fontenot #define DR_ENTITY_UNUSABLE 2 311ab519a01SNathan Fontenot #define ALLOCATION_STATE 9003 312ab519a01SNathan Fontenot #define ALLOC_UNUSABLE 0 313ab519a01SNathan Fontenot #define ALLOC_USABLE 1 314ab519a01SNathan Fontenot #define ISOLATION_STATE 9001 315ab519a01SNathan Fontenot #define ISOLATE 0 316ab519a01SNathan Fontenot #define UNISOLATE 1 317ab519a01SNathan Fontenot 318ab519a01SNathan Fontenot int dlpar_acquire_drc(u32 drc_index) 319ab519a01SNathan Fontenot { 320ab519a01SNathan Fontenot int dr_status, rc; 321ab519a01SNathan Fontenot 322ab519a01SNathan Fontenot rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status, 323ab519a01SNathan Fontenot DR_ENTITY_SENSE, drc_index); 324ab519a01SNathan Fontenot if (rc || dr_status != DR_ENTITY_UNUSABLE) 325ab519a01SNathan Fontenot return -1; 326ab519a01SNathan Fontenot 327ab519a01SNathan Fontenot rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE); 328ab519a01SNathan Fontenot if (rc) 329ab519a01SNathan Fontenot return rc; 330ab519a01SNathan Fontenot 331ab519a01SNathan Fontenot rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 332ab519a01SNathan Fontenot if (rc) { 333ab519a01SNathan Fontenot rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 334ab519a01SNathan Fontenot return rc; 335ab519a01SNathan Fontenot } 336ab519a01SNathan Fontenot 337ab519a01SNathan Fontenot return 0; 338ab519a01SNathan Fontenot } 339ab519a01SNathan Fontenot 340ab519a01SNathan Fontenot int dlpar_release_drc(u32 drc_index) 341ab519a01SNathan Fontenot { 342ab519a01SNathan Fontenot int dr_status, rc; 343ab519a01SNathan Fontenot 344ab519a01SNathan Fontenot rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status, 345ab519a01SNathan Fontenot DR_ENTITY_SENSE, drc_index); 346ab519a01SNathan Fontenot if (rc || dr_status != DR_ENTITY_PRESENT) 347ab519a01SNathan Fontenot return -1; 348ab519a01SNathan Fontenot 349ab519a01SNathan Fontenot rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE); 350ab519a01SNathan Fontenot if (rc) 351ab519a01SNathan Fontenot return rc; 352ab519a01SNathan Fontenot 353ab519a01SNathan Fontenot rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 354ab519a01SNathan Fontenot if (rc) { 355ab519a01SNathan Fontenot rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 356ab519a01SNathan Fontenot return rc; 357ab519a01SNathan Fontenot } 358ab519a01SNathan Fontenot 359ab519a01SNathan Fontenot return 0; 360ab519a01SNathan Fontenot } 361ab519a01SNathan Fontenot 3621a8061c4SNathan Fontenot #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE 363ab519a01SNathan Fontenot 364275a64f6SNathan Fontenot static int dlpar_online_cpu(struct device_node *dn) 365275a64f6SNathan Fontenot { 366275a64f6SNathan Fontenot int rc = 0; 367275a64f6SNathan Fontenot unsigned int cpu; 368275a64f6SNathan Fontenot int len, nthreads, i; 369822e7122SThomas Falcon const __be32 *intserv; 370822e7122SThomas Falcon u32 thread; 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++) { 380822e7122SThomas Falcon thread = be32_to_cpu(intserv[i]); 381275a64f6SNathan Fontenot for_each_present_cpu(cpu) { 382822e7122SThomas Falcon if (get_hard_smp_processor_id(cpu) != thread) 383275a64f6SNathan Fontenot continue; 384275a64f6SNathan Fontenot BUG_ON(get_cpu_current_state(cpu) 385275a64f6SNathan Fontenot != CPU_STATE_OFFLINE); 386275a64f6SNathan Fontenot cpu_maps_update_done(); 38710ccaf17SDan Streetman rc = device_online(get_cpu_device(cpu)); 388275a64f6SNathan Fontenot if (rc) 389275a64f6SNathan Fontenot goto out; 390275a64f6SNathan Fontenot cpu_maps_update_begin(); 391275a64f6SNathan Fontenot 392275a64f6SNathan Fontenot break; 393275a64f6SNathan Fontenot } 394275a64f6SNathan Fontenot if (cpu == num_possible_cpus()) 395275a64f6SNathan Fontenot printk(KERN_WARNING "Could not find cpu to online " 396822e7122SThomas Falcon "with physical id 0x%x\n", thread); 397275a64f6SNathan Fontenot } 398275a64f6SNathan Fontenot cpu_maps_update_done(); 399275a64f6SNathan Fontenot 400275a64f6SNathan Fontenot out: 401275a64f6SNathan Fontenot return rc; 402275a64f6SNathan Fontenot 403275a64f6SNathan Fontenot } 404275a64f6SNathan Fontenot 4051a8061c4SNathan Fontenot static ssize_t dlpar_cpu_probe(const char *buf, size_t count) 4061a8061c4SNathan Fontenot { 4078d5ff320STyrel Datwyler struct device_node *dn, *parent; 4081618bd53SDaniel Walter u32 drc_index; 4091a8061c4SNathan Fontenot int rc; 4101a8061c4SNathan Fontenot 4111618bd53SDaniel Walter rc = kstrtou32(buf, 0, &drc_index); 4126dedcca6SToshi Kani if (rc) 4136dedcca6SToshi Kani return -EINVAL; 4141a8061c4SNathan Fontenot 4158d5ff320STyrel Datwyler parent = of_find_node_by_path("/cpus"); 4166dedcca6SToshi Kani if (!parent) 4176dedcca6SToshi Kani return -ENODEV; 4188d5ff320STyrel Datwyler 419d6f1e7abSBharata B Rao dn = dlpar_configure_connector(cpu_to_be32(drc_index), parent); 4206dedcca6SToshi Kani if (!dn) 4216dedcca6SToshi Kani return -EINVAL; 4221a8061c4SNathan Fontenot 4238d5ff320STyrel Datwyler of_node_put(parent); 4241a8061c4SNathan Fontenot 4251a8061c4SNathan Fontenot rc = dlpar_acquire_drc(drc_index); 4261a8061c4SNathan Fontenot if (rc) { 4271a8061c4SNathan Fontenot dlpar_free_cc_nodes(dn); 4286dedcca6SToshi Kani return -EINVAL; 4291a8061c4SNathan Fontenot } 4301a8061c4SNathan Fontenot 4311a8061c4SNathan Fontenot rc = dlpar_attach_node(dn); 4321a8061c4SNathan Fontenot if (rc) { 4331a8061c4SNathan Fontenot dlpar_release_drc(drc_index); 4341a8061c4SNathan Fontenot dlpar_free_cc_nodes(dn); 4356dedcca6SToshi Kani return rc; 4361a8061c4SNathan Fontenot } 4371a8061c4SNathan Fontenot 438275a64f6SNathan Fontenot rc = dlpar_online_cpu(dn); 4396dedcca6SToshi Kani if (rc) 4406dedcca6SToshi Kani return rc; 441b6db63d1SGautham R Shenoy 4426dedcca6SToshi Kani return count; 4431a8061c4SNathan Fontenot } 4441a8061c4SNathan Fontenot 445275a64f6SNathan Fontenot static int dlpar_offline_cpu(struct device_node *dn) 446275a64f6SNathan Fontenot { 447275a64f6SNathan Fontenot int rc = 0; 448275a64f6SNathan Fontenot unsigned int cpu; 449275a64f6SNathan Fontenot int len, nthreads, i; 450e36d1227SThomas Falcon const __be32 *intserv; 451e36d1227SThomas Falcon u32 thread; 452275a64f6SNathan Fontenot 453275a64f6SNathan Fontenot intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len); 454275a64f6SNathan Fontenot if (!intserv) 455275a64f6SNathan Fontenot return -EINVAL; 456275a64f6SNathan Fontenot 457275a64f6SNathan Fontenot nthreads = len / sizeof(u32); 458275a64f6SNathan Fontenot 459275a64f6SNathan Fontenot cpu_maps_update_begin(); 460275a64f6SNathan Fontenot for (i = 0; i < nthreads; i++) { 461e36d1227SThomas Falcon thread = be32_to_cpu(intserv[i]); 462275a64f6SNathan Fontenot for_each_present_cpu(cpu) { 463e36d1227SThomas Falcon if (get_hard_smp_processor_id(cpu) != thread) 464275a64f6SNathan Fontenot continue; 465275a64f6SNathan Fontenot 466275a64f6SNathan Fontenot if (get_cpu_current_state(cpu) == CPU_STATE_OFFLINE) 467275a64f6SNathan Fontenot break; 468275a64f6SNathan Fontenot 469275a64f6SNathan Fontenot if (get_cpu_current_state(cpu) == CPU_STATE_ONLINE) { 470ceddee23SRobert Jennings set_preferred_offline_state(cpu, CPU_STATE_OFFLINE); 471275a64f6SNathan Fontenot cpu_maps_update_done(); 47210ccaf17SDan Streetman rc = device_offline(get_cpu_device(cpu)); 473275a64f6SNathan Fontenot if (rc) 474275a64f6SNathan Fontenot goto out; 475275a64f6SNathan Fontenot cpu_maps_update_begin(); 476275a64f6SNathan Fontenot break; 477275a64f6SNathan Fontenot 478275a64f6SNathan Fontenot } 479275a64f6SNathan Fontenot 480275a64f6SNathan Fontenot /* 481275a64f6SNathan Fontenot * The cpu is in CPU_STATE_INACTIVE. 482275a64f6SNathan Fontenot * Upgrade it's state to CPU_STATE_OFFLINE. 483275a64f6SNathan Fontenot */ 484275a64f6SNathan Fontenot set_preferred_offline_state(cpu, CPU_STATE_OFFLINE); 485e36d1227SThomas Falcon BUG_ON(plpar_hcall_norets(H_PROD, thread) 486275a64f6SNathan Fontenot != H_SUCCESS); 487275a64f6SNathan Fontenot __cpu_die(cpu); 488275a64f6SNathan Fontenot break; 489275a64f6SNathan Fontenot } 490275a64f6SNathan Fontenot if (cpu == num_possible_cpus()) 491275a64f6SNathan Fontenot printk(KERN_WARNING "Could not find cpu to offline " 492e36d1227SThomas Falcon "with physical id 0x%x\n", thread); 493275a64f6SNathan Fontenot } 494275a64f6SNathan Fontenot cpu_maps_update_done(); 495275a64f6SNathan Fontenot 496275a64f6SNathan Fontenot out: 497275a64f6SNathan Fontenot return rc; 498275a64f6SNathan Fontenot 499275a64f6SNathan Fontenot } 500275a64f6SNathan Fontenot 5011a8061c4SNathan Fontenot static ssize_t dlpar_cpu_release(const char *buf, size_t count) 5021a8061c4SNathan Fontenot { 5031a8061c4SNathan Fontenot struct device_node *dn; 504e36d1227SThomas Falcon u32 drc_index; 5051a8061c4SNathan Fontenot int rc; 5061a8061c4SNathan Fontenot 5071a8061c4SNathan Fontenot dn = of_find_node_by_path(buf); 5081a8061c4SNathan Fontenot if (!dn) 5091a8061c4SNathan Fontenot return -EINVAL; 5101a8061c4SNathan Fontenot 511e36d1227SThomas Falcon rc = of_property_read_u32(dn, "ibm,my-drc-index", &drc_index); 512e36d1227SThomas Falcon if (rc) { 5131a8061c4SNathan Fontenot of_node_put(dn); 5141a8061c4SNathan Fontenot return -EINVAL; 5151a8061c4SNathan Fontenot } 5161a8061c4SNathan Fontenot 517275a64f6SNathan Fontenot rc = dlpar_offline_cpu(dn); 518b6db63d1SGautham R Shenoy if (rc) { 519b6db63d1SGautham R Shenoy of_node_put(dn); 5206dedcca6SToshi Kani return -EINVAL; 521b6db63d1SGautham R Shenoy } 522b6db63d1SGautham R Shenoy 523e36d1227SThomas Falcon rc = dlpar_release_drc(drc_index); 5241a8061c4SNathan Fontenot if (rc) { 5251a8061c4SNathan Fontenot of_node_put(dn); 5266dedcca6SToshi Kani return rc; 5271a8061c4SNathan Fontenot } 5281a8061c4SNathan Fontenot 5291a8061c4SNathan Fontenot rc = dlpar_detach_node(dn); 5301a8061c4SNathan Fontenot if (rc) { 531e36d1227SThomas Falcon dlpar_acquire_drc(drc_index); 5326dedcca6SToshi Kani return rc; 5331a8061c4SNathan Fontenot } 5341a8061c4SNathan Fontenot 5351a8061c4SNathan Fontenot of_node_put(dn); 5366dedcca6SToshi Kani 5376dedcca6SToshi Kani return count; 5381a8061c4SNathan Fontenot } 5391a8061c4SNathan Fontenot 540*999e2dadSNathan Fontenot #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ 541*999e2dadSNathan Fontenot 542*999e2dadSNathan Fontenot static int handle_dlpar_errorlog(struct pseries_hp_errorlog *hp_elog) 543*999e2dadSNathan Fontenot { 544*999e2dadSNathan Fontenot int rc; 545*999e2dadSNathan Fontenot 546*999e2dadSNathan Fontenot /* pseries error logs are in BE format, convert to cpu type */ 547*999e2dadSNathan Fontenot switch (hp_elog->id_type) { 548*999e2dadSNathan Fontenot case PSERIES_HP_ELOG_ID_DRC_COUNT: 549*999e2dadSNathan Fontenot hp_elog->_drc_u.drc_count = 550*999e2dadSNathan Fontenot be32_to_cpu(hp_elog->_drc_u.drc_count); 551*999e2dadSNathan Fontenot break; 552*999e2dadSNathan Fontenot case PSERIES_HP_ELOG_ID_DRC_INDEX: 553*999e2dadSNathan Fontenot hp_elog->_drc_u.drc_index = 554*999e2dadSNathan Fontenot be32_to_cpu(hp_elog->_drc_u.drc_index); 555*999e2dadSNathan Fontenot } 556*999e2dadSNathan Fontenot 557*999e2dadSNathan Fontenot switch (hp_elog->resource) { 558*999e2dadSNathan Fontenot case PSERIES_HP_ELOG_RESOURCE_MEM: 559*999e2dadSNathan Fontenot rc = dlpar_memory(hp_elog); 560*999e2dadSNathan Fontenot break; 561*999e2dadSNathan Fontenot default: 562*999e2dadSNathan Fontenot pr_warn_ratelimited("Invalid resource (%d) specified\n", 563*999e2dadSNathan Fontenot hp_elog->resource); 564*999e2dadSNathan Fontenot rc = -EINVAL; 565*999e2dadSNathan Fontenot } 566*999e2dadSNathan Fontenot 567*999e2dadSNathan Fontenot return rc; 568*999e2dadSNathan Fontenot } 569*999e2dadSNathan Fontenot 570*999e2dadSNathan Fontenot static ssize_t dlpar_store(struct class *class, struct class_attribute *attr, 571*999e2dadSNathan Fontenot const char *buf, size_t count) 572*999e2dadSNathan Fontenot { 573*999e2dadSNathan Fontenot struct pseries_hp_errorlog *hp_elog; 574*999e2dadSNathan Fontenot const char *arg; 575*999e2dadSNathan Fontenot int rc; 576*999e2dadSNathan Fontenot 577*999e2dadSNathan Fontenot hp_elog = kzalloc(sizeof(*hp_elog), GFP_KERNEL); 578*999e2dadSNathan Fontenot if (!hp_elog) { 579*999e2dadSNathan Fontenot rc = -ENOMEM; 580*999e2dadSNathan Fontenot goto dlpar_store_out; 581*999e2dadSNathan Fontenot } 582*999e2dadSNathan Fontenot 583*999e2dadSNathan Fontenot /* Parse out the request from the user, this will be in the form 584*999e2dadSNathan Fontenot * <resource> <action> <id_type> <id> 585*999e2dadSNathan Fontenot */ 586*999e2dadSNathan Fontenot arg = buf; 587*999e2dadSNathan Fontenot if (!strncmp(arg, "memory", 6)) { 588*999e2dadSNathan Fontenot hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_MEM; 589*999e2dadSNathan Fontenot arg += strlen("memory "); 590*999e2dadSNathan Fontenot } else { 591*999e2dadSNathan Fontenot pr_err("Invalid resource specified: \"%s\"\n", buf); 592*999e2dadSNathan Fontenot rc = -EINVAL; 593*999e2dadSNathan Fontenot goto dlpar_store_out; 594*999e2dadSNathan Fontenot } 595*999e2dadSNathan Fontenot 596*999e2dadSNathan Fontenot if (!strncmp(arg, "add", 3)) { 597*999e2dadSNathan Fontenot hp_elog->action = PSERIES_HP_ELOG_ACTION_ADD; 598*999e2dadSNathan Fontenot arg += strlen("add "); 599*999e2dadSNathan Fontenot } else if (!strncmp(arg, "remove", 6)) { 600*999e2dadSNathan Fontenot hp_elog->action = PSERIES_HP_ELOG_ACTION_REMOVE; 601*999e2dadSNathan Fontenot arg += strlen("remove "); 602*999e2dadSNathan Fontenot } else { 603*999e2dadSNathan Fontenot pr_err("Invalid action specified: \"%s\"\n", buf); 604*999e2dadSNathan Fontenot rc = -EINVAL; 605*999e2dadSNathan Fontenot goto dlpar_store_out; 606*999e2dadSNathan Fontenot } 607*999e2dadSNathan Fontenot 608*999e2dadSNathan Fontenot if (!strncmp(arg, "index", 5)) { 609*999e2dadSNathan Fontenot u32 index; 610*999e2dadSNathan Fontenot 611*999e2dadSNathan Fontenot hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_INDEX; 612*999e2dadSNathan Fontenot arg += strlen("index "); 613*999e2dadSNathan Fontenot if (kstrtou32(arg, 0, &index)) { 614*999e2dadSNathan Fontenot rc = -EINVAL; 615*999e2dadSNathan Fontenot pr_err("Invalid drc_index specified: \"%s\"\n", buf); 616*999e2dadSNathan Fontenot goto dlpar_store_out; 617*999e2dadSNathan Fontenot } 618*999e2dadSNathan Fontenot 619*999e2dadSNathan Fontenot hp_elog->_drc_u.drc_index = cpu_to_be32(index); 620*999e2dadSNathan Fontenot } else if (!strncmp(arg, "count", 5)) { 621*999e2dadSNathan Fontenot u32 count; 622*999e2dadSNathan Fontenot 623*999e2dadSNathan Fontenot hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_COUNT; 624*999e2dadSNathan Fontenot arg += strlen("count "); 625*999e2dadSNathan Fontenot if (kstrtou32(arg, 0, &count)) { 626*999e2dadSNathan Fontenot rc = -EINVAL; 627*999e2dadSNathan Fontenot pr_err("Invalid count specified: \"%s\"\n", buf); 628*999e2dadSNathan Fontenot goto dlpar_store_out; 629*999e2dadSNathan Fontenot } 630*999e2dadSNathan Fontenot 631*999e2dadSNathan Fontenot hp_elog->_drc_u.drc_count = cpu_to_be32(count); 632*999e2dadSNathan Fontenot } else { 633*999e2dadSNathan Fontenot pr_err("Invalid id_type specified: \"%s\"\n", buf); 634*999e2dadSNathan Fontenot rc = -EINVAL; 635*999e2dadSNathan Fontenot goto dlpar_store_out; 636*999e2dadSNathan Fontenot } 637*999e2dadSNathan Fontenot 638*999e2dadSNathan Fontenot rc = handle_dlpar_errorlog(hp_elog); 639*999e2dadSNathan Fontenot 640*999e2dadSNathan Fontenot dlpar_store_out: 641*999e2dadSNathan Fontenot kfree(hp_elog); 642*999e2dadSNathan Fontenot return rc ? rc : count; 643*999e2dadSNathan Fontenot } 644*999e2dadSNathan Fontenot 645*999e2dadSNathan Fontenot static CLASS_ATTR(dlpar, S_IWUSR, NULL, dlpar_store); 646*999e2dadSNathan Fontenot 6471a8061c4SNathan Fontenot static int __init pseries_dlpar_init(void) 6481a8061c4SNathan Fontenot { 649*999e2dadSNathan Fontenot int rc; 650*999e2dadSNathan Fontenot 651*999e2dadSNathan Fontenot #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE 6521a8061c4SNathan Fontenot ppc_md.cpu_probe = dlpar_cpu_probe; 6531a8061c4SNathan Fontenot ppc_md.cpu_release = dlpar_cpu_release; 654*999e2dadSNathan Fontenot #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ 6551a8061c4SNathan Fontenot 656*999e2dadSNathan Fontenot rc = sysfs_create_file(kernel_kobj, &class_attr_dlpar.attr); 657*999e2dadSNathan Fontenot 658*999e2dadSNathan Fontenot return rc; 6591a8061c4SNathan Fontenot } 6601a8061c4SNathan Fontenot machine_device_initcall(pseries, pseries_dlpar_init); 6611a8061c4SNathan Fontenot 662