1*ab519a01SNathan Fontenot /* 2*ab519a01SNathan Fontenot * Support for dynamic reconfiguration for PCI, Memory, and CPU 3*ab519a01SNathan Fontenot * Hotplug and Dynamic Logical Partitioning on RPA platforms. 4*ab519a01SNathan Fontenot * 5*ab519a01SNathan Fontenot * Copyright (C) 2009 Nathan Fontenot 6*ab519a01SNathan Fontenot * Copyright (C) 2009 IBM Corporation 7*ab519a01SNathan Fontenot * 8*ab519a01SNathan Fontenot * This program is free software; you can redistribute it and/or 9*ab519a01SNathan Fontenot * modify it under the terms of the GNU General Public License version 10*ab519a01SNathan Fontenot * 2 as published by the Free Software Foundation. 11*ab519a01SNathan Fontenot */ 12*ab519a01SNathan Fontenot 13*ab519a01SNathan Fontenot #include <linux/kernel.h> 14*ab519a01SNathan Fontenot #include <linux/kref.h> 15*ab519a01SNathan Fontenot #include <linux/notifier.h> 16*ab519a01SNathan Fontenot #include <linux/proc_fs.h> 17*ab519a01SNathan Fontenot #include <linux/spinlock.h> 18*ab519a01SNathan Fontenot #include <linux/cpu.h> 19*ab519a01SNathan Fontenot 20*ab519a01SNathan Fontenot #include <asm/prom.h> 21*ab519a01SNathan Fontenot #include <asm/machdep.h> 22*ab519a01SNathan Fontenot #include <asm/uaccess.h> 23*ab519a01SNathan Fontenot #include <asm/rtas.h> 24*ab519a01SNathan Fontenot #include <asm/pSeries_reconfig.h> 25*ab519a01SNathan Fontenot 26*ab519a01SNathan Fontenot struct cc_workarea { 27*ab519a01SNathan Fontenot u32 drc_index; 28*ab519a01SNathan Fontenot u32 zero; 29*ab519a01SNathan Fontenot u32 name_offset; 30*ab519a01SNathan Fontenot u32 prop_length; 31*ab519a01SNathan Fontenot u32 prop_offset; 32*ab519a01SNathan Fontenot }; 33*ab519a01SNathan Fontenot 34*ab519a01SNathan Fontenot static void dlpar_free_cc_property(struct property *prop) 35*ab519a01SNathan Fontenot { 36*ab519a01SNathan Fontenot kfree(prop->name); 37*ab519a01SNathan Fontenot kfree(prop->value); 38*ab519a01SNathan Fontenot kfree(prop); 39*ab519a01SNathan Fontenot } 40*ab519a01SNathan Fontenot 41*ab519a01SNathan Fontenot static struct property *dlpar_parse_cc_property(struct cc_workarea *ccwa) 42*ab519a01SNathan Fontenot { 43*ab519a01SNathan Fontenot struct property *prop; 44*ab519a01SNathan Fontenot char *name; 45*ab519a01SNathan Fontenot char *value; 46*ab519a01SNathan Fontenot 47*ab519a01SNathan Fontenot prop = kzalloc(sizeof(*prop), GFP_KERNEL); 48*ab519a01SNathan Fontenot if (!prop) 49*ab519a01SNathan Fontenot return NULL; 50*ab519a01SNathan Fontenot 51*ab519a01SNathan Fontenot name = (char *)ccwa + ccwa->name_offset; 52*ab519a01SNathan Fontenot prop->name = kstrdup(name, GFP_KERNEL); 53*ab519a01SNathan Fontenot 54*ab519a01SNathan Fontenot prop->length = ccwa->prop_length; 55*ab519a01SNathan Fontenot value = (char *)ccwa + ccwa->prop_offset; 56*ab519a01SNathan Fontenot prop->value = kzalloc(prop->length, GFP_KERNEL); 57*ab519a01SNathan Fontenot if (!prop->value) { 58*ab519a01SNathan Fontenot dlpar_free_cc_property(prop); 59*ab519a01SNathan Fontenot return NULL; 60*ab519a01SNathan Fontenot } 61*ab519a01SNathan Fontenot 62*ab519a01SNathan Fontenot memcpy(prop->value, value, prop->length); 63*ab519a01SNathan Fontenot return prop; 64*ab519a01SNathan Fontenot } 65*ab519a01SNathan Fontenot 66*ab519a01SNathan Fontenot static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa) 67*ab519a01SNathan Fontenot { 68*ab519a01SNathan Fontenot struct device_node *dn; 69*ab519a01SNathan Fontenot char *name; 70*ab519a01SNathan Fontenot 71*ab519a01SNathan Fontenot dn = kzalloc(sizeof(*dn), GFP_KERNEL); 72*ab519a01SNathan Fontenot if (!dn) 73*ab519a01SNathan Fontenot return NULL; 74*ab519a01SNathan Fontenot 75*ab519a01SNathan Fontenot /* The configure connector reported name does not contain a 76*ab519a01SNathan Fontenot * preceeding '/', so we allocate a buffer large enough to 77*ab519a01SNathan Fontenot * prepend this to the full_name. 78*ab519a01SNathan Fontenot */ 79*ab519a01SNathan Fontenot name = (char *)ccwa + ccwa->name_offset; 80*ab519a01SNathan Fontenot dn->full_name = kmalloc(strlen(name) + 2, GFP_KERNEL); 81*ab519a01SNathan Fontenot if (!dn->full_name) { 82*ab519a01SNathan Fontenot kfree(dn); 83*ab519a01SNathan Fontenot return NULL; 84*ab519a01SNathan Fontenot } 85*ab519a01SNathan Fontenot 86*ab519a01SNathan Fontenot sprintf(dn->full_name, "/%s", name); 87*ab519a01SNathan Fontenot return dn; 88*ab519a01SNathan Fontenot } 89*ab519a01SNathan Fontenot 90*ab519a01SNathan Fontenot static void dlpar_free_one_cc_node(struct device_node *dn) 91*ab519a01SNathan Fontenot { 92*ab519a01SNathan Fontenot struct property *prop; 93*ab519a01SNathan Fontenot 94*ab519a01SNathan Fontenot while (dn->properties) { 95*ab519a01SNathan Fontenot prop = dn->properties; 96*ab519a01SNathan Fontenot dn->properties = prop->next; 97*ab519a01SNathan Fontenot dlpar_free_cc_property(prop); 98*ab519a01SNathan Fontenot } 99*ab519a01SNathan Fontenot 100*ab519a01SNathan Fontenot kfree(dn->full_name); 101*ab519a01SNathan Fontenot kfree(dn); 102*ab519a01SNathan Fontenot } 103*ab519a01SNathan Fontenot 104*ab519a01SNathan Fontenot static void dlpar_free_cc_nodes(struct device_node *dn) 105*ab519a01SNathan Fontenot { 106*ab519a01SNathan Fontenot if (dn->child) 107*ab519a01SNathan Fontenot dlpar_free_cc_nodes(dn->child); 108*ab519a01SNathan Fontenot 109*ab519a01SNathan Fontenot if (dn->sibling) 110*ab519a01SNathan Fontenot dlpar_free_cc_nodes(dn->sibling); 111*ab519a01SNathan Fontenot 112*ab519a01SNathan Fontenot dlpar_free_one_cc_node(dn); 113*ab519a01SNathan Fontenot } 114*ab519a01SNathan Fontenot 115*ab519a01SNathan Fontenot #define NEXT_SIBLING 1 116*ab519a01SNathan Fontenot #define NEXT_CHILD 2 117*ab519a01SNathan Fontenot #define NEXT_PROPERTY 3 118*ab519a01SNathan Fontenot #define PREV_PARENT 4 119*ab519a01SNathan Fontenot #define MORE_MEMORY 5 120*ab519a01SNathan Fontenot #define CALL_AGAIN -2 121*ab519a01SNathan Fontenot #define ERR_CFG_USE -9003 122*ab519a01SNathan Fontenot 123*ab519a01SNathan Fontenot struct device_node *dlpar_configure_connector(u32 drc_index) 124*ab519a01SNathan Fontenot { 125*ab519a01SNathan Fontenot struct device_node *dn; 126*ab519a01SNathan Fontenot struct device_node *first_dn = NULL; 127*ab519a01SNathan Fontenot struct device_node *last_dn = NULL; 128*ab519a01SNathan Fontenot struct property *property; 129*ab519a01SNathan Fontenot struct property *last_property = NULL; 130*ab519a01SNathan Fontenot struct cc_workarea *ccwa; 131*ab519a01SNathan Fontenot int cc_token; 132*ab519a01SNathan Fontenot int rc; 133*ab519a01SNathan Fontenot 134*ab519a01SNathan Fontenot cc_token = rtas_token("ibm,configure-connector"); 135*ab519a01SNathan Fontenot if (cc_token == RTAS_UNKNOWN_SERVICE) 136*ab519a01SNathan Fontenot return NULL; 137*ab519a01SNathan Fontenot 138*ab519a01SNathan Fontenot spin_lock(&rtas_data_buf_lock); 139*ab519a01SNathan Fontenot ccwa = (struct cc_workarea *)&rtas_data_buf[0]; 140*ab519a01SNathan Fontenot ccwa->drc_index = drc_index; 141*ab519a01SNathan Fontenot ccwa->zero = 0; 142*ab519a01SNathan Fontenot 143*ab519a01SNathan Fontenot rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL); 144*ab519a01SNathan Fontenot while (rc) { 145*ab519a01SNathan Fontenot switch (rc) { 146*ab519a01SNathan Fontenot case NEXT_SIBLING: 147*ab519a01SNathan Fontenot dn = dlpar_parse_cc_node(ccwa); 148*ab519a01SNathan Fontenot if (!dn) 149*ab519a01SNathan Fontenot goto cc_error; 150*ab519a01SNathan Fontenot 151*ab519a01SNathan Fontenot dn->parent = last_dn->parent; 152*ab519a01SNathan Fontenot last_dn->sibling = dn; 153*ab519a01SNathan Fontenot last_dn = dn; 154*ab519a01SNathan Fontenot break; 155*ab519a01SNathan Fontenot 156*ab519a01SNathan Fontenot case NEXT_CHILD: 157*ab519a01SNathan Fontenot dn = dlpar_parse_cc_node(ccwa); 158*ab519a01SNathan Fontenot if (!dn) 159*ab519a01SNathan Fontenot goto cc_error; 160*ab519a01SNathan Fontenot 161*ab519a01SNathan Fontenot if (!first_dn) 162*ab519a01SNathan Fontenot first_dn = dn; 163*ab519a01SNathan Fontenot else { 164*ab519a01SNathan Fontenot dn->parent = last_dn; 165*ab519a01SNathan Fontenot if (last_dn) 166*ab519a01SNathan Fontenot last_dn->child = dn; 167*ab519a01SNathan Fontenot } 168*ab519a01SNathan Fontenot 169*ab519a01SNathan Fontenot last_dn = dn; 170*ab519a01SNathan Fontenot break; 171*ab519a01SNathan Fontenot 172*ab519a01SNathan Fontenot case NEXT_PROPERTY: 173*ab519a01SNathan Fontenot property = dlpar_parse_cc_property(ccwa); 174*ab519a01SNathan Fontenot if (!property) 175*ab519a01SNathan Fontenot goto cc_error; 176*ab519a01SNathan Fontenot 177*ab519a01SNathan Fontenot if (!last_dn->properties) 178*ab519a01SNathan Fontenot last_dn->properties = property; 179*ab519a01SNathan Fontenot else 180*ab519a01SNathan Fontenot last_property->next = property; 181*ab519a01SNathan Fontenot 182*ab519a01SNathan Fontenot last_property = property; 183*ab519a01SNathan Fontenot break; 184*ab519a01SNathan Fontenot 185*ab519a01SNathan Fontenot case PREV_PARENT: 186*ab519a01SNathan Fontenot last_dn = last_dn->parent; 187*ab519a01SNathan Fontenot break; 188*ab519a01SNathan Fontenot 189*ab519a01SNathan Fontenot case CALL_AGAIN: 190*ab519a01SNathan Fontenot break; 191*ab519a01SNathan Fontenot 192*ab519a01SNathan Fontenot case MORE_MEMORY: 193*ab519a01SNathan Fontenot case ERR_CFG_USE: 194*ab519a01SNathan Fontenot default: 195*ab519a01SNathan Fontenot printk(KERN_ERR "Unexpected Error (%d) " 196*ab519a01SNathan Fontenot "returned from configure-connector\n", rc); 197*ab519a01SNathan Fontenot goto cc_error; 198*ab519a01SNathan Fontenot } 199*ab519a01SNathan Fontenot 200*ab519a01SNathan Fontenot rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL); 201*ab519a01SNathan Fontenot } 202*ab519a01SNathan Fontenot 203*ab519a01SNathan Fontenot spin_unlock(&rtas_data_buf_lock); 204*ab519a01SNathan Fontenot return first_dn; 205*ab519a01SNathan Fontenot 206*ab519a01SNathan Fontenot cc_error: 207*ab519a01SNathan Fontenot if (first_dn) 208*ab519a01SNathan Fontenot dlpar_free_cc_nodes(first_dn); 209*ab519a01SNathan Fontenot spin_unlock(&rtas_data_buf_lock); 210*ab519a01SNathan Fontenot return NULL; 211*ab519a01SNathan Fontenot } 212*ab519a01SNathan Fontenot 213*ab519a01SNathan Fontenot static struct device_node *derive_parent(const char *path) 214*ab519a01SNathan Fontenot { 215*ab519a01SNathan Fontenot struct device_node *parent; 216*ab519a01SNathan Fontenot char *last_slash; 217*ab519a01SNathan Fontenot 218*ab519a01SNathan Fontenot last_slash = strrchr(path, '/'); 219*ab519a01SNathan Fontenot if (last_slash == path) { 220*ab519a01SNathan Fontenot parent = of_find_node_by_path("/"); 221*ab519a01SNathan Fontenot } else { 222*ab519a01SNathan Fontenot char *parent_path; 223*ab519a01SNathan Fontenot int parent_path_len = last_slash - path + 1; 224*ab519a01SNathan Fontenot parent_path = kmalloc(parent_path_len, GFP_KERNEL); 225*ab519a01SNathan Fontenot if (!parent_path) 226*ab519a01SNathan Fontenot return NULL; 227*ab519a01SNathan Fontenot 228*ab519a01SNathan Fontenot strlcpy(parent_path, path, parent_path_len); 229*ab519a01SNathan Fontenot parent = of_find_node_by_path(parent_path); 230*ab519a01SNathan Fontenot kfree(parent_path); 231*ab519a01SNathan Fontenot } 232*ab519a01SNathan Fontenot 233*ab519a01SNathan Fontenot return parent; 234*ab519a01SNathan Fontenot } 235*ab519a01SNathan Fontenot 236*ab519a01SNathan Fontenot int dlpar_attach_node(struct device_node *dn) 237*ab519a01SNathan Fontenot { 238*ab519a01SNathan Fontenot struct proc_dir_entry *ent; 239*ab519a01SNathan Fontenot int rc; 240*ab519a01SNathan Fontenot 241*ab519a01SNathan Fontenot of_node_set_flag(dn, OF_DYNAMIC); 242*ab519a01SNathan Fontenot kref_init(&dn->kref); 243*ab519a01SNathan Fontenot dn->parent = derive_parent(dn->full_name); 244*ab519a01SNathan Fontenot if (!dn->parent) 245*ab519a01SNathan Fontenot return -ENOMEM; 246*ab519a01SNathan Fontenot 247*ab519a01SNathan Fontenot rc = blocking_notifier_call_chain(&pSeries_reconfig_chain, 248*ab519a01SNathan Fontenot PSERIES_RECONFIG_ADD, dn); 249*ab519a01SNathan Fontenot if (rc == NOTIFY_BAD) { 250*ab519a01SNathan Fontenot printk(KERN_ERR "Failed to add device node %s\n", 251*ab519a01SNathan Fontenot dn->full_name); 252*ab519a01SNathan Fontenot return -ENOMEM; /* For now, safe to assume kmalloc failure */ 253*ab519a01SNathan Fontenot } 254*ab519a01SNathan Fontenot 255*ab519a01SNathan Fontenot of_attach_node(dn); 256*ab519a01SNathan Fontenot 257*ab519a01SNathan Fontenot #ifdef CONFIG_PROC_DEVICETREE 258*ab519a01SNathan Fontenot ent = proc_mkdir(strrchr(dn->full_name, '/') + 1, dn->parent->pde); 259*ab519a01SNathan Fontenot if (ent) 260*ab519a01SNathan Fontenot proc_device_tree_add_node(dn, ent); 261*ab519a01SNathan Fontenot #endif 262*ab519a01SNathan Fontenot 263*ab519a01SNathan Fontenot of_node_put(dn->parent); 264*ab519a01SNathan Fontenot return 0; 265*ab519a01SNathan Fontenot } 266*ab519a01SNathan Fontenot 267*ab519a01SNathan Fontenot int dlpar_detach_node(struct device_node *dn) 268*ab519a01SNathan Fontenot { 269*ab519a01SNathan Fontenot struct device_node *parent = dn->parent; 270*ab519a01SNathan Fontenot struct property *prop = dn->properties; 271*ab519a01SNathan Fontenot 272*ab519a01SNathan Fontenot #ifdef CONFIG_PROC_DEVICETREE 273*ab519a01SNathan Fontenot while (prop) { 274*ab519a01SNathan Fontenot remove_proc_entry(prop->name, dn->pde); 275*ab519a01SNathan Fontenot prop = prop->next; 276*ab519a01SNathan Fontenot } 277*ab519a01SNathan Fontenot 278*ab519a01SNathan Fontenot if (dn->pde) 279*ab519a01SNathan Fontenot remove_proc_entry(dn->pde->name, parent->pde); 280*ab519a01SNathan Fontenot #endif 281*ab519a01SNathan Fontenot 282*ab519a01SNathan Fontenot blocking_notifier_call_chain(&pSeries_reconfig_chain, 283*ab519a01SNathan Fontenot PSERIES_RECONFIG_REMOVE, dn); 284*ab519a01SNathan Fontenot of_detach_node(dn); 285*ab519a01SNathan Fontenot of_node_put(dn); /* Must decrement the refcount */ 286*ab519a01SNathan Fontenot 287*ab519a01SNathan Fontenot return 0; 288*ab519a01SNathan Fontenot } 289*ab519a01SNathan Fontenot 290*ab519a01SNathan Fontenot #define DR_ENTITY_SENSE 9003 291*ab519a01SNathan Fontenot #define DR_ENTITY_PRESENT 1 292*ab519a01SNathan Fontenot #define DR_ENTITY_UNUSABLE 2 293*ab519a01SNathan Fontenot #define ALLOCATION_STATE 9003 294*ab519a01SNathan Fontenot #define ALLOC_UNUSABLE 0 295*ab519a01SNathan Fontenot #define ALLOC_USABLE 1 296*ab519a01SNathan Fontenot #define ISOLATION_STATE 9001 297*ab519a01SNathan Fontenot #define ISOLATE 0 298*ab519a01SNathan Fontenot #define UNISOLATE 1 299*ab519a01SNathan Fontenot 300*ab519a01SNathan Fontenot int dlpar_acquire_drc(u32 drc_index) 301*ab519a01SNathan Fontenot { 302*ab519a01SNathan Fontenot int dr_status, rc; 303*ab519a01SNathan Fontenot 304*ab519a01SNathan Fontenot rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status, 305*ab519a01SNathan Fontenot DR_ENTITY_SENSE, drc_index); 306*ab519a01SNathan Fontenot if (rc || dr_status != DR_ENTITY_UNUSABLE) 307*ab519a01SNathan Fontenot return -1; 308*ab519a01SNathan Fontenot 309*ab519a01SNathan Fontenot rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE); 310*ab519a01SNathan Fontenot if (rc) 311*ab519a01SNathan Fontenot return rc; 312*ab519a01SNathan Fontenot 313*ab519a01SNathan Fontenot rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 314*ab519a01SNathan Fontenot if (rc) { 315*ab519a01SNathan Fontenot rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 316*ab519a01SNathan Fontenot return rc; 317*ab519a01SNathan Fontenot } 318*ab519a01SNathan Fontenot 319*ab519a01SNathan Fontenot return 0; 320*ab519a01SNathan Fontenot } 321*ab519a01SNathan Fontenot 322*ab519a01SNathan Fontenot int dlpar_release_drc(u32 drc_index) 323*ab519a01SNathan Fontenot { 324*ab519a01SNathan Fontenot int dr_status, rc; 325*ab519a01SNathan Fontenot 326*ab519a01SNathan Fontenot rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status, 327*ab519a01SNathan Fontenot DR_ENTITY_SENSE, drc_index); 328*ab519a01SNathan Fontenot if (rc || dr_status != DR_ENTITY_PRESENT) 329*ab519a01SNathan Fontenot return -1; 330*ab519a01SNathan Fontenot 331*ab519a01SNathan Fontenot rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE); 332*ab519a01SNathan Fontenot if (rc) 333*ab519a01SNathan Fontenot return rc; 334*ab519a01SNathan Fontenot 335*ab519a01SNathan Fontenot rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 336*ab519a01SNathan Fontenot if (rc) { 337*ab519a01SNathan Fontenot rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 338*ab519a01SNathan Fontenot return rc; 339*ab519a01SNathan Fontenot } 340*ab519a01SNathan Fontenot 341*ab519a01SNathan Fontenot return 0; 342*ab519a01SNathan Fontenot } 343*ab519a01SNathan Fontenot 344*ab519a01SNathan Fontenot 345