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 13999e2dadSNathan Fontenot #define pr_fmt(fmt) "dlpar: " fmt 14999e2dadSNathan 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> 21*06bacefcSAndy Shevchenko 22*06bacefcSAndy Shevchenko #include "of_helpers.h" 23b6db63d1SGautham R Shenoy #include "offline_states.h" 241217d34bSAnton Blanchard #include "pseries.h" 25ab519a01SNathan Fontenot 26ab519a01SNathan Fontenot #include <asm/prom.h> 27ab519a01SNathan Fontenot #include <asm/machdep.h> 28ab519a01SNathan Fontenot #include <asm/uaccess.h> 29ab519a01SNathan Fontenot #include <asm/rtas.h> 30ab519a01SNathan Fontenot 31ab519a01SNathan Fontenot struct cc_workarea { 32d6f1e7abSBharata B Rao __be32 drc_index; 33d6f1e7abSBharata B Rao __be32 zero; 34d6f1e7abSBharata B Rao __be32 name_offset; 35d6f1e7abSBharata B Rao __be32 prop_length; 36d6f1e7abSBharata B Rao __be32 prop_offset; 37ab519a01SNathan Fontenot }; 38ab519a01SNathan Fontenot 3920648974SNathan Fontenot void dlpar_free_cc_property(struct property *prop) 40ab519a01SNathan Fontenot { 41ab519a01SNathan Fontenot kfree(prop->name); 42ab519a01SNathan Fontenot kfree(prop->value); 43ab519a01SNathan Fontenot kfree(prop); 44ab519a01SNathan Fontenot } 45ab519a01SNathan Fontenot 46ab519a01SNathan Fontenot static struct property *dlpar_parse_cc_property(struct cc_workarea *ccwa) 47ab519a01SNathan Fontenot { 48ab519a01SNathan Fontenot struct property *prop; 49ab519a01SNathan Fontenot char *name; 50ab519a01SNathan Fontenot char *value; 51ab519a01SNathan Fontenot 52ab519a01SNathan Fontenot prop = kzalloc(sizeof(*prop), GFP_KERNEL); 53ab519a01SNathan Fontenot if (!prop) 54ab519a01SNathan Fontenot return NULL; 55ab519a01SNathan Fontenot 56d6f1e7abSBharata B Rao name = (char *)ccwa + be32_to_cpu(ccwa->name_offset); 57ab519a01SNathan Fontenot prop->name = kstrdup(name, GFP_KERNEL); 58ab519a01SNathan Fontenot 59d6f1e7abSBharata B Rao prop->length = be32_to_cpu(ccwa->prop_length); 60d6f1e7abSBharata B Rao value = (char *)ccwa + be32_to_cpu(ccwa->prop_offset); 61e72ed6b5SNishanth Aravamudan prop->value = kmemdup(value, prop->length, GFP_KERNEL); 62ab519a01SNathan Fontenot if (!prop->value) { 63ab519a01SNathan Fontenot dlpar_free_cc_property(prop); 64ab519a01SNathan Fontenot return NULL; 65ab519a01SNathan Fontenot } 66ab519a01SNathan Fontenot 67ab519a01SNathan Fontenot return prop; 68ab519a01SNathan Fontenot } 69ab519a01SNathan Fontenot 708d5ff320STyrel Datwyler static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa, 718d5ff320STyrel Datwyler const char *path) 72ab519a01SNathan Fontenot { 73ab519a01SNathan Fontenot struct device_node *dn; 74ab519a01SNathan Fontenot char *name; 75ab519a01SNathan Fontenot 768d5ff320STyrel Datwyler /* If parent node path is "/" advance path to NULL terminator to 778d5ff320STyrel Datwyler * prevent double leading slashs in full_name. 788d5ff320STyrel Datwyler */ 798d5ff320STyrel Datwyler if (!path[1]) 808d5ff320STyrel Datwyler path++; 818d5ff320STyrel Datwyler 82ab519a01SNathan Fontenot dn = kzalloc(sizeof(*dn), GFP_KERNEL); 83ab519a01SNathan Fontenot if (!dn) 84ab519a01SNathan Fontenot return NULL; 85ab519a01SNathan Fontenot 86d6f1e7abSBharata B Rao name = (char *)ccwa + be32_to_cpu(ccwa->name_offset); 878d5ff320STyrel Datwyler dn->full_name = kasprintf(GFP_KERNEL, "%s/%s", path, name); 88ab519a01SNathan Fontenot if (!dn->full_name) { 89ab519a01SNathan Fontenot kfree(dn); 90ab519a01SNathan Fontenot return NULL; 91ab519a01SNathan Fontenot } 92ab519a01SNathan Fontenot 931578cb76STyrel Datwyler of_node_set_flag(dn, OF_DYNAMIC); 9497a9a717STyrel Datwyler of_node_init(dn); 951578cb76STyrel Datwyler 96ab519a01SNathan Fontenot return dn; 97ab519a01SNathan Fontenot } 98ab519a01SNathan Fontenot 99ab519a01SNathan Fontenot static void dlpar_free_one_cc_node(struct device_node *dn) 100ab519a01SNathan Fontenot { 101ab519a01SNathan Fontenot struct property *prop; 102ab519a01SNathan Fontenot 103ab519a01SNathan Fontenot while (dn->properties) { 104ab519a01SNathan Fontenot prop = dn->properties; 105ab519a01SNathan Fontenot dn->properties = prop->next; 106ab519a01SNathan Fontenot dlpar_free_cc_property(prop); 107ab519a01SNathan Fontenot } 108ab519a01SNathan Fontenot 109ab519a01SNathan Fontenot kfree(dn->full_name); 110ab519a01SNathan Fontenot kfree(dn); 111ab519a01SNathan Fontenot } 112ab519a01SNathan Fontenot 11320648974SNathan Fontenot void dlpar_free_cc_nodes(struct device_node *dn) 114ab519a01SNathan Fontenot { 115ab519a01SNathan Fontenot if (dn->child) 116ab519a01SNathan Fontenot dlpar_free_cc_nodes(dn->child); 117ab519a01SNathan Fontenot 118ab519a01SNathan Fontenot if (dn->sibling) 119ab519a01SNathan Fontenot dlpar_free_cc_nodes(dn->sibling); 120ab519a01SNathan Fontenot 121ab519a01SNathan Fontenot dlpar_free_one_cc_node(dn); 122ab519a01SNathan Fontenot } 123ab519a01SNathan Fontenot 1249c740025SAnton Blanchard #define COMPLETE 0 125ab519a01SNathan Fontenot #define NEXT_SIBLING 1 126ab519a01SNathan Fontenot #define NEXT_CHILD 2 127ab519a01SNathan Fontenot #define NEXT_PROPERTY 3 128ab519a01SNathan Fontenot #define PREV_PARENT 4 129ab519a01SNathan Fontenot #define MORE_MEMORY 5 130ab519a01SNathan Fontenot #define CALL_AGAIN -2 131ab519a01SNathan Fontenot #define ERR_CFG_USE -9003 132ab519a01SNathan Fontenot 133d6f1e7abSBharata B Rao struct device_node *dlpar_configure_connector(__be32 drc_index, 1348d5ff320STyrel Datwyler struct device_node *parent) 135ab519a01SNathan Fontenot { 136ab519a01SNathan Fontenot struct device_node *dn; 137ab519a01SNathan Fontenot struct device_node *first_dn = NULL; 138ab519a01SNathan Fontenot struct device_node *last_dn = NULL; 139ab519a01SNathan Fontenot struct property *property; 140ab519a01SNathan Fontenot struct property *last_property = NULL; 141ab519a01SNathan Fontenot struct cc_workarea *ccwa; 14293f68f1eSNathan Fontenot char *data_buf; 1438d5ff320STyrel Datwyler const char *parent_path = parent->full_name; 144ab519a01SNathan Fontenot int cc_token; 14593f68f1eSNathan Fontenot int rc = -1; 146ab519a01SNathan Fontenot 147ab519a01SNathan Fontenot cc_token = rtas_token("ibm,configure-connector"); 148ab519a01SNathan Fontenot if (cc_token == RTAS_UNKNOWN_SERVICE) 149ab519a01SNathan Fontenot return NULL; 150ab519a01SNathan Fontenot 15193f68f1eSNathan Fontenot data_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL); 15293f68f1eSNathan Fontenot if (!data_buf) 15393f68f1eSNathan Fontenot return NULL; 15493f68f1eSNathan Fontenot 15593f68f1eSNathan Fontenot ccwa = (struct cc_workarea *)&data_buf[0]; 156ab519a01SNathan Fontenot ccwa->drc_index = drc_index; 157ab519a01SNathan Fontenot ccwa->zero = 0; 158ab519a01SNathan Fontenot 15993f68f1eSNathan Fontenot do { 16093f68f1eSNathan Fontenot /* Since we release the rtas_data_buf lock between configure 16193f68f1eSNathan Fontenot * connector calls we want to re-populate the rtas_data_buffer 16293f68f1eSNathan Fontenot * with the contents of the previous call. 16393f68f1eSNathan Fontenot */ 16493f68f1eSNathan Fontenot spin_lock(&rtas_data_buf_lock); 16593f68f1eSNathan Fontenot 16693f68f1eSNathan Fontenot memcpy(rtas_data_buf, data_buf, RTAS_DATA_BUF_SIZE); 167ab519a01SNathan Fontenot rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL); 16893f68f1eSNathan Fontenot memcpy(data_buf, rtas_data_buf, RTAS_DATA_BUF_SIZE); 16993f68f1eSNathan Fontenot 17093f68f1eSNathan Fontenot spin_unlock(&rtas_data_buf_lock); 17193f68f1eSNathan Fontenot 172ab519a01SNathan Fontenot switch (rc) { 1739c740025SAnton Blanchard case COMPLETE: 1749c740025SAnton Blanchard break; 1759c740025SAnton Blanchard 176ab519a01SNathan Fontenot case NEXT_SIBLING: 1778d5ff320STyrel Datwyler dn = dlpar_parse_cc_node(ccwa, parent_path); 178ab519a01SNathan Fontenot if (!dn) 179ab519a01SNathan Fontenot goto cc_error; 180ab519a01SNathan Fontenot 181ab519a01SNathan Fontenot dn->parent = last_dn->parent; 182ab519a01SNathan Fontenot last_dn->sibling = dn; 183ab519a01SNathan Fontenot last_dn = dn; 184ab519a01SNathan Fontenot break; 185ab519a01SNathan Fontenot 186ab519a01SNathan Fontenot case NEXT_CHILD: 1878d5ff320STyrel Datwyler if (first_dn) 1888d5ff320STyrel Datwyler parent_path = last_dn->full_name; 1898d5ff320STyrel Datwyler 1908d5ff320STyrel Datwyler dn = dlpar_parse_cc_node(ccwa, parent_path); 191ab519a01SNathan Fontenot if (!dn) 192ab519a01SNathan Fontenot goto cc_error; 193ab519a01SNathan Fontenot 1948d5ff320STyrel Datwyler if (!first_dn) { 1958d5ff320STyrel Datwyler dn->parent = parent; 196ab519a01SNathan Fontenot first_dn = dn; 1978d5ff320STyrel Datwyler } else { 198ab519a01SNathan Fontenot dn->parent = last_dn; 199ab519a01SNathan Fontenot if (last_dn) 200ab519a01SNathan Fontenot last_dn->child = dn; 201ab519a01SNathan Fontenot } 202ab519a01SNathan Fontenot 203ab519a01SNathan Fontenot last_dn = dn; 204ab519a01SNathan Fontenot break; 205ab519a01SNathan Fontenot 206ab519a01SNathan Fontenot case NEXT_PROPERTY: 207ab519a01SNathan Fontenot property = dlpar_parse_cc_property(ccwa); 208ab519a01SNathan Fontenot if (!property) 209ab519a01SNathan Fontenot goto cc_error; 210ab519a01SNathan Fontenot 211ab519a01SNathan Fontenot if (!last_dn->properties) 212ab519a01SNathan Fontenot last_dn->properties = property; 213ab519a01SNathan Fontenot else 214ab519a01SNathan Fontenot last_property->next = property; 215ab519a01SNathan Fontenot 216ab519a01SNathan Fontenot last_property = property; 217ab519a01SNathan Fontenot break; 218ab519a01SNathan Fontenot 219ab519a01SNathan Fontenot case PREV_PARENT: 220ab519a01SNathan Fontenot last_dn = last_dn->parent; 2218d5ff320STyrel Datwyler parent_path = last_dn->parent->full_name; 222ab519a01SNathan Fontenot break; 223ab519a01SNathan Fontenot 224ab519a01SNathan Fontenot case CALL_AGAIN: 225ab519a01SNathan Fontenot break; 226ab519a01SNathan Fontenot 227ab519a01SNathan Fontenot case MORE_MEMORY: 228ab519a01SNathan Fontenot case ERR_CFG_USE: 229ab519a01SNathan Fontenot default: 230ab519a01SNathan Fontenot printk(KERN_ERR "Unexpected Error (%d) " 231ab519a01SNathan Fontenot "returned from configure-connector\n", rc); 232ab519a01SNathan Fontenot goto cc_error; 233ab519a01SNathan Fontenot } 23493f68f1eSNathan Fontenot } while (rc); 235ab519a01SNathan Fontenot 236ab519a01SNathan Fontenot cc_error: 23793f68f1eSNathan Fontenot kfree(data_buf); 23893f68f1eSNathan Fontenot 23993f68f1eSNathan Fontenot if (rc) { 240ab519a01SNathan Fontenot if (first_dn) 241ab519a01SNathan Fontenot dlpar_free_cc_nodes(first_dn); 24293f68f1eSNathan Fontenot 243ab519a01SNathan Fontenot return NULL; 244ab519a01SNathan Fontenot } 245ab519a01SNathan Fontenot 24693f68f1eSNathan Fontenot return first_dn; 24793f68f1eSNathan Fontenot } 24893f68f1eSNathan Fontenot 249ab519a01SNathan Fontenot int dlpar_attach_node(struct device_node *dn) 250ab519a01SNathan Fontenot { 251ab519a01SNathan Fontenot int rc; 252ab519a01SNathan Fontenot 253*06bacefcSAndy Shevchenko dn->parent = pseries_of_derive_parent(dn->full_name); 254*06bacefcSAndy Shevchenko if (IS_ERR(dn->parent)) 255*06bacefcSAndy Shevchenko return PTR_ERR(dn->parent); 256ab519a01SNathan Fontenot 2571cf3d8b3SNathan Fontenot rc = of_attach_node(dn); 2583aef19f0SAkinobu Mita if (rc) { 259ab519a01SNathan Fontenot printk(KERN_ERR "Failed to add device node %s\n", 260ab519a01SNathan Fontenot dn->full_name); 2613aef19f0SAkinobu Mita return rc; 262ab519a01SNathan Fontenot } 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 { 2705935ff43STyrel Datwyler struct device_node *child; 2711cf3d8b3SNathan Fontenot int rc; 272ab519a01SNathan Fontenot 2735935ff43STyrel Datwyler child = of_get_next_child(dn, NULL); 2745935ff43STyrel Datwyler while (child) { 2755935ff43STyrel Datwyler dlpar_detach_node(child); 2765935ff43STyrel Datwyler child = of_get_next_child(dn, child); 2775935ff43STyrel Datwyler } 2785935ff43STyrel Datwyler 2791cf3d8b3SNathan Fontenot rc = of_detach_node(dn); 2801cf3d8b3SNathan Fontenot if (rc) 2811cf3d8b3SNathan Fontenot return rc; 2821cf3d8b3SNathan Fontenot 2831cf3d8b3SNathan Fontenot of_node_put(dn); /* Must decrement the refcount */ 284ab519a01SNathan Fontenot return 0; 285ab519a01SNathan Fontenot } 286ab519a01SNathan Fontenot 287ab519a01SNathan Fontenot #define DR_ENTITY_SENSE 9003 288ab519a01SNathan Fontenot #define DR_ENTITY_PRESENT 1 289ab519a01SNathan Fontenot #define DR_ENTITY_UNUSABLE 2 290ab519a01SNathan Fontenot #define ALLOCATION_STATE 9003 291ab519a01SNathan Fontenot #define ALLOC_UNUSABLE 0 292ab519a01SNathan Fontenot #define ALLOC_USABLE 1 293ab519a01SNathan Fontenot #define ISOLATION_STATE 9001 294ab519a01SNathan Fontenot #define ISOLATE 0 295ab519a01SNathan Fontenot #define UNISOLATE 1 296ab519a01SNathan Fontenot 297ab519a01SNathan Fontenot int dlpar_acquire_drc(u32 drc_index) 298ab519a01SNathan Fontenot { 299ab519a01SNathan Fontenot int dr_status, rc; 300ab519a01SNathan Fontenot 301ab519a01SNathan Fontenot rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status, 302ab519a01SNathan Fontenot DR_ENTITY_SENSE, drc_index); 303ab519a01SNathan Fontenot if (rc || dr_status != DR_ENTITY_UNUSABLE) 304ab519a01SNathan Fontenot return -1; 305ab519a01SNathan Fontenot 306ab519a01SNathan Fontenot rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE); 307ab519a01SNathan Fontenot if (rc) 308ab519a01SNathan Fontenot return rc; 309ab519a01SNathan Fontenot 310ab519a01SNathan Fontenot rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 311ab519a01SNathan Fontenot if (rc) { 312ab519a01SNathan Fontenot rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 313ab519a01SNathan Fontenot return rc; 314ab519a01SNathan Fontenot } 315ab519a01SNathan Fontenot 316ab519a01SNathan Fontenot return 0; 317ab519a01SNathan Fontenot } 318ab519a01SNathan Fontenot 319ab519a01SNathan Fontenot int dlpar_release_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_PRESENT) 326ab519a01SNathan Fontenot return -1; 327ab519a01SNathan Fontenot 328ab519a01SNathan Fontenot rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE); 329ab519a01SNathan Fontenot if (rc) 330ab519a01SNathan Fontenot return rc; 331ab519a01SNathan Fontenot 332ab519a01SNathan Fontenot rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 333ab519a01SNathan Fontenot if (rc) { 334ab519a01SNathan Fontenot rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 335ab519a01SNathan Fontenot return rc; 336ab519a01SNathan Fontenot } 337ab519a01SNathan Fontenot 338ab519a01SNathan Fontenot return 0; 339ab519a01SNathan Fontenot } 340ab519a01SNathan Fontenot 3411a8061c4SNathan Fontenot #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE 342ab519a01SNathan Fontenot 343275a64f6SNathan Fontenot static int dlpar_online_cpu(struct device_node *dn) 344275a64f6SNathan Fontenot { 345275a64f6SNathan Fontenot int rc = 0; 346275a64f6SNathan Fontenot unsigned int cpu; 347275a64f6SNathan Fontenot int len, nthreads, i; 348822e7122SThomas Falcon const __be32 *intserv; 349822e7122SThomas Falcon u32 thread; 350275a64f6SNathan Fontenot 351275a64f6SNathan Fontenot intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len); 352275a64f6SNathan Fontenot if (!intserv) 353275a64f6SNathan Fontenot return -EINVAL; 354275a64f6SNathan Fontenot 355275a64f6SNathan Fontenot nthreads = len / sizeof(u32); 356275a64f6SNathan Fontenot 357275a64f6SNathan Fontenot cpu_maps_update_begin(); 358275a64f6SNathan Fontenot for (i = 0; i < nthreads; i++) { 359822e7122SThomas Falcon thread = be32_to_cpu(intserv[i]); 360275a64f6SNathan Fontenot for_each_present_cpu(cpu) { 361822e7122SThomas Falcon if (get_hard_smp_processor_id(cpu) != thread) 362275a64f6SNathan Fontenot continue; 363275a64f6SNathan Fontenot BUG_ON(get_cpu_current_state(cpu) 364275a64f6SNathan Fontenot != CPU_STATE_OFFLINE); 365275a64f6SNathan Fontenot cpu_maps_update_done(); 36610ccaf17SDan Streetman rc = device_online(get_cpu_device(cpu)); 367275a64f6SNathan Fontenot if (rc) 368275a64f6SNathan Fontenot goto out; 369275a64f6SNathan Fontenot cpu_maps_update_begin(); 370275a64f6SNathan Fontenot 371275a64f6SNathan Fontenot break; 372275a64f6SNathan Fontenot } 373275a64f6SNathan Fontenot if (cpu == num_possible_cpus()) 374275a64f6SNathan Fontenot printk(KERN_WARNING "Could not find cpu to online " 375822e7122SThomas Falcon "with physical id 0x%x\n", thread); 376275a64f6SNathan Fontenot } 377275a64f6SNathan Fontenot cpu_maps_update_done(); 378275a64f6SNathan Fontenot 379275a64f6SNathan Fontenot out: 380275a64f6SNathan Fontenot return rc; 381275a64f6SNathan Fontenot 382275a64f6SNathan Fontenot } 383275a64f6SNathan Fontenot 3841a8061c4SNathan Fontenot static ssize_t dlpar_cpu_probe(const char *buf, size_t count) 3851a8061c4SNathan Fontenot { 3868d5ff320STyrel Datwyler struct device_node *dn, *parent; 3871618bd53SDaniel Walter u32 drc_index; 3881a8061c4SNathan Fontenot int rc; 3891a8061c4SNathan Fontenot 3901618bd53SDaniel Walter rc = kstrtou32(buf, 0, &drc_index); 3916dedcca6SToshi Kani if (rc) 3926dedcca6SToshi Kani return -EINVAL; 3931a8061c4SNathan Fontenot 394f32393c9SNathan Fontenot rc = dlpar_acquire_drc(drc_index); 395f32393c9SNathan Fontenot if (rc) 396f32393c9SNathan Fontenot return -EINVAL; 397f32393c9SNathan Fontenot 3988d5ff320STyrel Datwyler parent = of_find_node_by_path("/cpus"); 3996dedcca6SToshi Kani if (!parent) 4006dedcca6SToshi Kani return -ENODEV; 4018d5ff320STyrel Datwyler 402d6f1e7abSBharata B Rao dn = dlpar_configure_connector(cpu_to_be32(drc_index), parent); 4032222ce0fSNathan Fontenot of_node_put(parent); 404daebaabbSBharata B Rao if (!dn) { 405daebaabbSBharata B Rao dlpar_release_drc(drc_index); 4066dedcca6SToshi Kani return -EINVAL; 407daebaabbSBharata B Rao } 4081a8061c4SNathan Fontenot 4091a8061c4SNathan Fontenot rc = dlpar_attach_node(dn); 4101a8061c4SNathan Fontenot if (rc) { 4111a8061c4SNathan Fontenot dlpar_release_drc(drc_index); 4121a8061c4SNathan Fontenot dlpar_free_cc_nodes(dn); 4136dedcca6SToshi Kani return rc; 4141a8061c4SNathan Fontenot } 4151a8061c4SNathan Fontenot 416275a64f6SNathan Fontenot rc = dlpar_online_cpu(dn); 4176dedcca6SToshi Kani if (rc) 4186dedcca6SToshi Kani return rc; 419b6db63d1SGautham R Shenoy 4206dedcca6SToshi Kani return count; 4211a8061c4SNathan Fontenot } 4221a8061c4SNathan Fontenot 423275a64f6SNathan Fontenot static int dlpar_offline_cpu(struct device_node *dn) 424275a64f6SNathan Fontenot { 425275a64f6SNathan Fontenot int rc = 0; 426275a64f6SNathan Fontenot unsigned int cpu; 427275a64f6SNathan Fontenot int len, nthreads, i; 428e36d1227SThomas Falcon const __be32 *intserv; 429e36d1227SThomas Falcon u32 thread; 430275a64f6SNathan Fontenot 431275a64f6SNathan Fontenot intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len); 432275a64f6SNathan Fontenot if (!intserv) 433275a64f6SNathan Fontenot return -EINVAL; 434275a64f6SNathan Fontenot 435275a64f6SNathan Fontenot nthreads = len / sizeof(u32); 436275a64f6SNathan Fontenot 437275a64f6SNathan Fontenot cpu_maps_update_begin(); 438275a64f6SNathan Fontenot for (i = 0; i < nthreads; i++) { 439e36d1227SThomas Falcon thread = be32_to_cpu(intserv[i]); 440275a64f6SNathan Fontenot for_each_present_cpu(cpu) { 441e36d1227SThomas Falcon if (get_hard_smp_processor_id(cpu) != thread) 442275a64f6SNathan Fontenot continue; 443275a64f6SNathan Fontenot 444275a64f6SNathan Fontenot if (get_cpu_current_state(cpu) == CPU_STATE_OFFLINE) 445275a64f6SNathan Fontenot break; 446275a64f6SNathan Fontenot 447275a64f6SNathan Fontenot if (get_cpu_current_state(cpu) == CPU_STATE_ONLINE) { 448ceddee23SRobert Jennings set_preferred_offline_state(cpu, CPU_STATE_OFFLINE); 449275a64f6SNathan Fontenot cpu_maps_update_done(); 45010ccaf17SDan Streetman rc = device_offline(get_cpu_device(cpu)); 451275a64f6SNathan Fontenot if (rc) 452275a64f6SNathan Fontenot goto out; 453275a64f6SNathan Fontenot cpu_maps_update_begin(); 454275a64f6SNathan Fontenot break; 455275a64f6SNathan Fontenot 456275a64f6SNathan Fontenot } 457275a64f6SNathan Fontenot 458275a64f6SNathan Fontenot /* 459275a64f6SNathan Fontenot * The cpu is in CPU_STATE_INACTIVE. 460275a64f6SNathan Fontenot * Upgrade it's state to CPU_STATE_OFFLINE. 461275a64f6SNathan Fontenot */ 462275a64f6SNathan Fontenot set_preferred_offline_state(cpu, CPU_STATE_OFFLINE); 463e36d1227SThomas Falcon BUG_ON(plpar_hcall_norets(H_PROD, thread) 464275a64f6SNathan Fontenot != H_SUCCESS); 465275a64f6SNathan Fontenot __cpu_die(cpu); 466275a64f6SNathan Fontenot break; 467275a64f6SNathan Fontenot } 468275a64f6SNathan Fontenot if (cpu == num_possible_cpus()) 469275a64f6SNathan Fontenot printk(KERN_WARNING "Could not find cpu to offline " 470e36d1227SThomas Falcon "with physical id 0x%x\n", thread); 471275a64f6SNathan Fontenot } 472275a64f6SNathan Fontenot cpu_maps_update_done(); 473275a64f6SNathan Fontenot 474275a64f6SNathan Fontenot out: 475275a64f6SNathan Fontenot return rc; 476275a64f6SNathan Fontenot 477275a64f6SNathan Fontenot } 478275a64f6SNathan Fontenot 4791a8061c4SNathan Fontenot static ssize_t dlpar_cpu_release(const char *buf, size_t count) 4801a8061c4SNathan Fontenot { 4811a8061c4SNathan Fontenot struct device_node *dn; 482e36d1227SThomas Falcon u32 drc_index; 4831a8061c4SNathan Fontenot int rc; 4841a8061c4SNathan Fontenot 4851a8061c4SNathan Fontenot dn = of_find_node_by_path(buf); 4861a8061c4SNathan Fontenot if (!dn) 4871a8061c4SNathan Fontenot return -EINVAL; 4881a8061c4SNathan Fontenot 489e36d1227SThomas Falcon rc = of_property_read_u32(dn, "ibm,my-drc-index", &drc_index); 490e36d1227SThomas Falcon if (rc) { 4911a8061c4SNathan Fontenot of_node_put(dn); 4921a8061c4SNathan Fontenot return -EINVAL; 4931a8061c4SNathan Fontenot } 4941a8061c4SNathan Fontenot 495275a64f6SNathan Fontenot rc = dlpar_offline_cpu(dn); 496b6db63d1SGautham R Shenoy if (rc) { 497b6db63d1SGautham R Shenoy of_node_put(dn); 4986dedcca6SToshi Kani return -EINVAL; 499b6db63d1SGautham R Shenoy } 500b6db63d1SGautham R Shenoy 501e36d1227SThomas Falcon rc = dlpar_release_drc(drc_index); 5021a8061c4SNathan Fontenot if (rc) { 5031a8061c4SNathan Fontenot of_node_put(dn); 5046dedcca6SToshi Kani return rc; 5051a8061c4SNathan Fontenot } 5061a8061c4SNathan Fontenot 5071a8061c4SNathan Fontenot rc = dlpar_detach_node(dn); 5081a8061c4SNathan Fontenot if (rc) { 509e36d1227SThomas Falcon dlpar_acquire_drc(drc_index); 5106dedcca6SToshi Kani return rc; 5111a8061c4SNathan Fontenot } 5121a8061c4SNathan Fontenot 5131a8061c4SNathan Fontenot of_node_put(dn); 5146dedcca6SToshi Kani 5156dedcca6SToshi Kani return count; 5161a8061c4SNathan Fontenot } 5171a8061c4SNathan Fontenot 518999e2dadSNathan Fontenot #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ 519999e2dadSNathan Fontenot 520999e2dadSNathan Fontenot static int handle_dlpar_errorlog(struct pseries_hp_errorlog *hp_elog) 521999e2dadSNathan Fontenot { 522999e2dadSNathan Fontenot int rc; 523999e2dadSNathan Fontenot 524999e2dadSNathan Fontenot /* pseries error logs are in BE format, convert to cpu type */ 525999e2dadSNathan Fontenot switch (hp_elog->id_type) { 526999e2dadSNathan Fontenot case PSERIES_HP_ELOG_ID_DRC_COUNT: 527999e2dadSNathan Fontenot hp_elog->_drc_u.drc_count = 528999e2dadSNathan Fontenot be32_to_cpu(hp_elog->_drc_u.drc_count); 529999e2dadSNathan Fontenot break; 530999e2dadSNathan Fontenot case PSERIES_HP_ELOG_ID_DRC_INDEX: 531999e2dadSNathan Fontenot hp_elog->_drc_u.drc_index = 532999e2dadSNathan Fontenot be32_to_cpu(hp_elog->_drc_u.drc_index); 533999e2dadSNathan Fontenot } 534999e2dadSNathan Fontenot 535999e2dadSNathan Fontenot switch (hp_elog->resource) { 536999e2dadSNathan Fontenot case PSERIES_HP_ELOG_RESOURCE_MEM: 537999e2dadSNathan Fontenot rc = dlpar_memory(hp_elog); 538999e2dadSNathan Fontenot break; 539999e2dadSNathan Fontenot default: 540999e2dadSNathan Fontenot pr_warn_ratelimited("Invalid resource (%d) specified\n", 541999e2dadSNathan Fontenot hp_elog->resource); 542999e2dadSNathan Fontenot rc = -EINVAL; 543999e2dadSNathan Fontenot } 544999e2dadSNathan Fontenot 545999e2dadSNathan Fontenot return rc; 546999e2dadSNathan Fontenot } 547999e2dadSNathan Fontenot 548999e2dadSNathan Fontenot static ssize_t dlpar_store(struct class *class, struct class_attribute *attr, 549999e2dadSNathan Fontenot const char *buf, size_t count) 550999e2dadSNathan Fontenot { 551999e2dadSNathan Fontenot struct pseries_hp_errorlog *hp_elog; 552999e2dadSNathan Fontenot const char *arg; 553999e2dadSNathan Fontenot int rc; 554999e2dadSNathan Fontenot 555999e2dadSNathan Fontenot hp_elog = kzalloc(sizeof(*hp_elog), GFP_KERNEL); 556999e2dadSNathan Fontenot if (!hp_elog) { 557999e2dadSNathan Fontenot rc = -ENOMEM; 558999e2dadSNathan Fontenot goto dlpar_store_out; 559999e2dadSNathan Fontenot } 560999e2dadSNathan Fontenot 561999e2dadSNathan Fontenot /* Parse out the request from the user, this will be in the form 562999e2dadSNathan Fontenot * <resource> <action> <id_type> <id> 563999e2dadSNathan Fontenot */ 564999e2dadSNathan Fontenot arg = buf; 565999e2dadSNathan Fontenot if (!strncmp(arg, "memory", 6)) { 566999e2dadSNathan Fontenot hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_MEM; 567999e2dadSNathan Fontenot arg += strlen("memory "); 568999e2dadSNathan Fontenot } else { 569999e2dadSNathan Fontenot pr_err("Invalid resource specified: \"%s\"\n", buf); 570999e2dadSNathan Fontenot rc = -EINVAL; 571999e2dadSNathan Fontenot goto dlpar_store_out; 572999e2dadSNathan Fontenot } 573999e2dadSNathan Fontenot 574999e2dadSNathan Fontenot if (!strncmp(arg, "add", 3)) { 575999e2dadSNathan Fontenot hp_elog->action = PSERIES_HP_ELOG_ACTION_ADD; 576999e2dadSNathan Fontenot arg += strlen("add "); 577999e2dadSNathan Fontenot } else if (!strncmp(arg, "remove", 6)) { 578999e2dadSNathan Fontenot hp_elog->action = PSERIES_HP_ELOG_ACTION_REMOVE; 579999e2dadSNathan Fontenot arg += strlen("remove "); 580999e2dadSNathan Fontenot } else { 581999e2dadSNathan Fontenot pr_err("Invalid action specified: \"%s\"\n", buf); 582999e2dadSNathan Fontenot rc = -EINVAL; 583999e2dadSNathan Fontenot goto dlpar_store_out; 584999e2dadSNathan Fontenot } 585999e2dadSNathan Fontenot 586999e2dadSNathan Fontenot if (!strncmp(arg, "index", 5)) { 587999e2dadSNathan Fontenot u32 index; 588999e2dadSNathan Fontenot 589999e2dadSNathan Fontenot hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_INDEX; 590999e2dadSNathan Fontenot arg += strlen("index "); 591999e2dadSNathan Fontenot if (kstrtou32(arg, 0, &index)) { 592999e2dadSNathan Fontenot rc = -EINVAL; 593999e2dadSNathan Fontenot pr_err("Invalid drc_index specified: \"%s\"\n", buf); 594999e2dadSNathan Fontenot goto dlpar_store_out; 595999e2dadSNathan Fontenot } 596999e2dadSNathan Fontenot 597999e2dadSNathan Fontenot hp_elog->_drc_u.drc_index = cpu_to_be32(index); 598999e2dadSNathan Fontenot } else if (!strncmp(arg, "count", 5)) { 599999e2dadSNathan Fontenot u32 count; 600999e2dadSNathan Fontenot 601999e2dadSNathan Fontenot hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_COUNT; 602999e2dadSNathan Fontenot arg += strlen("count "); 603999e2dadSNathan Fontenot if (kstrtou32(arg, 0, &count)) { 604999e2dadSNathan Fontenot rc = -EINVAL; 605999e2dadSNathan Fontenot pr_err("Invalid count specified: \"%s\"\n", buf); 606999e2dadSNathan Fontenot goto dlpar_store_out; 607999e2dadSNathan Fontenot } 608999e2dadSNathan Fontenot 609999e2dadSNathan Fontenot hp_elog->_drc_u.drc_count = cpu_to_be32(count); 610999e2dadSNathan Fontenot } else { 611999e2dadSNathan Fontenot pr_err("Invalid id_type specified: \"%s\"\n", buf); 612999e2dadSNathan Fontenot rc = -EINVAL; 613999e2dadSNathan Fontenot goto dlpar_store_out; 614999e2dadSNathan Fontenot } 615999e2dadSNathan Fontenot 616999e2dadSNathan Fontenot rc = handle_dlpar_errorlog(hp_elog); 617999e2dadSNathan Fontenot 618999e2dadSNathan Fontenot dlpar_store_out: 619999e2dadSNathan Fontenot kfree(hp_elog); 620999e2dadSNathan Fontenot return rc ? rc : count; 621999e2dadSNathan Fontenot } 622999e2dadSNathan Fontenot 623999e2dadSNathan Fontenot static CLASS_ATTR(dlpar, S_IWUSR, NULL, dlpar_store); 624999e2dadSNathan Fontenot 6251a8061c4SNathan Fontenot static int __init pseries_dlpar_init(void) 6261a8061c4SNathan Fontenot { 627999e2dadSNathan Fontenot int rc; 628999e2dadSNathan Fontenot 629999e2dadSNathan Fontenot #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE 6301a8061c4SNathan Fontenot ppc_md.cpu_probe = dlpar_cpu_probe; 6311a8061c4SNathan Fontenot ppc_md.cpu_release = dlpar_cpu_release; 632999e2dadSNathan Fontenot #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ 6331a8061c4SNathan Fontenot 634999e2dadSNathan Fontenot rc = sysfs_create_file(kernel_kobj, &class_attr_dlpar.attr); 635999e2dadSNathan Fontenot 636999e2dadSNathan Fontenot return rc; 6371a8061c4SNathan Fontenot } 6381a8061c4SNathan Fontenot machine_device_initcall(pseries, pseries_dlpar_init); 6391a8061c4SNathan Fontenot 640