linux/drivers/pci/hotplug/rpadlpar_core.c
<<
>>
Prefs
   1/*
   2 * Interface for Dynamic Logical Partitioning of I/O Slots on
   3 * RPA-compliant PPC64 platform.
   4 *
   5 * John Rose <johnrose@austin.ibm.com>
   6 * Linda Xie <lxie@us.ibm.com>
   7 *
   8 * October 2003
   9 *
  10 * Copyright (C) 2003 IBM.
  11 *
  12 *      This program is free software; you can redistribute it and/or
  13 *      modify it under the terms of the GNU General Public License
  14 *      as published by the Free Software Foundation; either version
  15 *      2 of the License, or (at your option) any later version.
  16 */
  17
  18#undef DEBUG
  19
  20#include <linux/init.h>
  21#include <linux/pci.h>
  22#include <linux/string.h>
  23#include <linux/vmalloc.h>
  24
  25#include <asm/pci-bridge.h>
  26#include <linux/mutex.h>
  27#include <asm/rtas.h>
  28#include <asm/vio.h>
  29
  30#include "../pci.h"
  31#include "rpaphp.h"
  32#include "rpadlpar.h"
  33
  34static DEFINE_MUTEX(rpadlpar_mutex);
  35
  36#define DLPAR_MODULE_NAME "rpadlpar_io"
  37
  38#define NODE_TYPE_VIO  1
  39#define NODE_TYPE_SLOT 2
  40#define NODE_TYPE_PHB  3
  41
  42static struct device_node *find_vio_slot_node(char *drc_name)
  43{
  44        struct device_node *parent = of_find_node_by_name(NULL, "vdevice");
  45        struct device_node *dn = NULL;
  46        char *name;
  47        int rc;
  48
  49        if (!parent)
  50                return NULL;
  51
  52        while ((dn = of_get_next_child(parent, dn))) {
  53                rc = rpaphp_get_drc_props(dn, NULL, &name, NULL, NULL);
  54                if ((rc == 0) && (!strcmp(drc_name, name)))
  55                        break;
  56        }
  57
  58        return dn;
  59}
  60
  61/* Find dlpar-capable pci node that contains the specified name and type */
  62static struct device_node *find_php_slot_pci_node(char *drc_name,
  63                                                  char *drc_type)
  64{
  65        struct device_node *np = NULL;
  66        char *name;
  67        char *type;
  68        int rc;
  69
  70        while ((np = of_find_node_by_name(np, "pci"))) {
  71                rc = rpaphp_get_drc_props(np, NULL, &name, &type, NULL);
  72                if (rc == 0)
  73                        if (!strcmp(drc_name, name) && !strcmp(drc_type, type))
  74                                break;
  75        }
  76
  77        return np;
  78}
  79
  80static struct device_node *find_dlpar_node(char *drc_name, int *node_type)
  81{
  82        struct device_node *dn;
  83
  84        dn = find_php_slot_pci_node(drc_name, "SLOT");
  85        if (dn) {
  86                *node_type = NODE_TYPE_SLOT;
  87                return dn;
  88        }
  89
  90        dn = find_php_slot_pci_node(drc_name, "PHB");
  91        if (dn) {
  92                *node_type = NODE_TYPE_PHB;
  93                return dn;
  94        }
  95
  96        dn = find_vio_slot_node(drc_name);
  97        if (dn) {
  98                *node_type = NODE_TYPE_VIO;
  99                return dn;
 100        }
 101
 102        return NULL;
 103}
 104
 105/**
 106 * find_php_slot - return hotplug slot structure for device node
 107 * @dn: target &device_node
 108 *
 109 * This routine will return the hotplug slot structure
 110 * for a given device node. Note that built-in PCI slots
 111 * may be dlpar-able, but not hot-pluggable, so this routine
 112 * will return NULL for built-in PCI slots.
 113 */
 114static struct slot *find_php_slot(struct device_node *dn)
 115{
 116        struct list_head *tmp, *n;
 117        struct slot *slot;
 118
 119        list_for_each_safe(tmp, n, &rpaphp_slot_head) {
 120                slot = list_entry(tmp, struct slot, rpaphp_slot_list);
 121                if (slot->dn == dn)
 122                        return slot;
 123        }
 124
 125        return NULL;
 126}
 127
 128static struct pci_dev *dlpar_find_new_dev(struct pci_bus *parent,
 129                                        struct device_node *dev_dn)
 130{
 131        struct pci_dev *tmp = NULL;
 132        struct device_node *child_dn;
 133
 134        list_for_each_entry(tmp, &parent->devices, bus_list) {
 135                child_dn = pci_device_to_OF_node(tmp);
 136                if (child_dn == dev_dn)
 137                        return tmp;
 138        }
 139        return NULL;
 140}
 141
 142static void dlpar_pci_add_bus(struct device_node *dn)
 143{
 144        struct pci_dn *pdn = PCI_DN(dn);
 145        struct pci_controller *phb = pdn->phb;
 146        struct pci_dev *dev = NULL;
 147
 148        eeh_add_device_tree_early(dn);
 149
 150        /* Add EADS device to PHB bus, adding new entry to bus->devices */
 151        dev = of_create_pci_dev(dn, phb->bus, pdn->devfn);
 152        if (!dev) {
 153                printk(KERN_ERR "%s: failed to create pci dev for %s\n",
 154                                __func__, dn->full_name);
 155                return;
 156        }
 157
 158        /* Scan below the new bridge */
 159        if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
 160            dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
 161                of_scan_pci_bridge(dn, dev);
 162
 163        /* Map IO space for child bus, which may or may not succeed */
 164        pcibios_map_io_space(dev->subordinate);
 165
 166        /* Finish adding it : resource allocation, adding devices, etc...
 167         * Note that we need to perform the finish pass on the -parent-
 168         * bus of the EADS bridge so the bridge device itself gets
 169         * properly added
 170         */
 171        pcibios_finish_adding_to_bus(phb->bus);
 172}
 173
 174static int dlpar_add_pci_slot(char *drc_name, struct device_node *dn)
 175{
 176        struct pci_dev *dev;
 177        struct pci_controller *phb;
 178
 179        if (pcibios_find_pci_bus(dn))
 180                return -EINVAL;
 181
 182        /* Add pci bus */
 183        dlpar_pci_add_bus(dn);
 184
 185        /* Confirm new bridge dev was created */
 186        phb = PCI_DN(dn)->phb;
 187        dev = dlpar_find_new_dev(phb->bus, dn);
 188
 189        if (!dev) {
 190                printk(KERN_ERR "%s: unable to add bus %s\n", __func__,
 191                        drc_name);
 192                return -EIO;
 193        }
 194
 195        if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
 196                printk(KERN_ERR "%s: unexpected header type %d, unable to add bus %s\n",
 197                        __func__, dev->hdr_type, drc_name);
 198                return -EIO;
 199        }
 200
 201        /* Add hotplug slot */
 202        if (rpaphp_add_slot(dn)) {
 203                printk(KERN_ERR "%s: unable to add hotplug slot %s\n",
 204                        __func__, drc_name);
 205                return -EIO;
 206        }
 207        return 0;
 208}
 209
 210static int dlpar_remove_phb(char *drc_name, struct device_node *dn)
 211{
 212        struct slot *slot;
 213        struct pci_dn *pdn;
 214        int rc = 0;
 215
 216        if (!pcibios_find_pci_bus(dn))
 217                return -EINVAL;
 218
 219        /* If pci slot is hotplugable, use hotplug to remove it */
 220        slot = find_php_slot(dn);
 221        if (slot && rpaphp_deregister_slot(slot)) {
 222                printk(KERN_ERR "%s: unable to remove hotplug slot %s\n",
 223                       __func__, drc_name);
 224                return -EIO;
 225        }
 226
 227        pdn = dn->data;
 228        BUG_ON(!pdn || !pdn->phb);
 229        rc = remove_phb_dynamic(pdn->phb);
 230        if (rc < 0)
 231                return rc;
 232
 233        pdn->phb = NULL;
 234
 235        return 0;
 236}
 237
 238static int dlpar_add_phb(char *drc_name, struct device_node *dn)
 239{
 240        struct pci_controller *phb;
 241
 242        if (PCI_DN(dn) && PCI_DN(dn)->phb) {
 243                /* PHB already exists */
 244                return -EINVAL;
 245        }
 246
 247        phb = init_phb_dynamic(dn);
 248        if (!phb)
 249                return -EIO;
 250
 251        if (rpaphp_add_slot(dn)) {
 252                printk(KERN_ERR "%s: unable to add hotplug slot %s\n",
 253                        __func__, drc_name);
 254                return -EIO;
 255        }
 256        return 0;
 257}
 258
 259static int dlpar_add_vio_slot(char *drc_name, struct device_node *dn)
 260{
 261        if (vio_find_node(dn))
 262                return -EINVAL;
 263
 264        if (!vio_register_device_node(dn)) {
 265                printk(KERN_ERR
 266                        "%s: failed to register vio node %s\n",
 267                        __func__, drc_name);
 268                return -EIO;
 269        }
 270        return 0;
 271}
 272
 273/**
 274 * dlpar_add_slot - DLPAR add an I/O Slot
 275 * @drc_name: drc-name of newly added slot
 276 *
 277 * Make the hotplug module and the kernel aware of a newly added I/O Slot.
 278 * Return Codes:
 279 * 0                    Success
 280 * -ENODEV              Not a valid drc_name
 281 * -EINVAL              Slot already added
 282 * -ERESTARTSYS         Signalled before obtaining lock
 283 * -EIO                 Internal PCI Error
 284 */
 285int dlpar_add_slot(char *drc_name)
 286{
 287        struct device_node *dn = NULL;
 288        int node_type;
 289        int rc = -EIO;
 290
 291        if (mutex_lock_interruptible(&rpadlpar_mutex))
 292                return -ERESTARTSYS;
 293
 294        /* Find newly added node */
 295        dn = find_dlpar_node(drc_name, &node_type);
 296        if (!dn) {
 297                rc = -ENODEV;
 298                goto exit;
 299        }
 300
 301        switch (node_type) {
 302                case NODE_TYPE_VIO:
 303                        rc = dlpar_add_vio_slot(drc_name, dn);
 304                        break;
 305                case NODE_TYPE_SLOT:
 306                        rc = dlpar_add_pci_slot(drc_name, dn);
 307                        break;
 308                case NODE_TYPE_PHB:
 309                        rc = dlpar_add_phb(drc_name, dn);
 310                        break;
 311        }
 312
 313        printk(KERN_INFO "%s: slot %s added\n", DLPAR_MODULE_NAME, drc_name);
 314exit:
 315        mutex_unlock(&rpadlpar_mutex);
 316        return rc;
 317}
 318
 319/**
 320 * dlpar_remove_vio_slot - DLPAR remove a virtual I/O Slot
 321 * @drc_name: drc-name of newly added slot
 322 * @dn: &device_node
 323 *
 324 * Remove the kernel and hotplug representations of an I/O Slot.
 325 * Return Codes:
 326 * 0                    Success
 327 * -EINVAL              Vio dev doesn't exist
 328 */
 329static int dlpar_remove_vio_slot(char *drc_name, struct device_node *dn)
 330{
 331        struct vio_dev *vio_dev;
 332
 333        vio_dev = vio_find_node(dn);
 334        if (!vio_dev)
 335                return -EINVAL;
 336
 337        vio_unregister_device(vio_dev);
 338        return 0;
 339}
 340
 341/**
 342 * dlpar_remove_pci_slot - DLPAR remove a PCI I/O Slot
 343 * @drc_name: drc-name of newly added slot
 344 * @dn: &device_node
 345 *
 346 * Remove the kernel and hotplug representations of a PCI I/O Slot.
 347 * Return Codes:
 348 * 0                    Success
 349 * -ENODEV              Not a valid drc_name
 350 * -EIO                 Internal PCI Error
 351 */
 352int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn)
 353{
 354        struct pci_bus *bus;
 355        struct slot *slot;
 356
 357        bus = pcibios_find_pci_bus(dn);
 358        if (!bus)
 359                return -EINVAL;
 360
 361        pr_debug("PCI: Removing PCI slot below EADS bridge %s\n",
 362                 bus->self ? pci_name(bus->self) : "<!PHB!>");
 363
 364        slot = find_php_slot(dn);
 365        if (slot) {
 366                pr_debug("PCI: Removing hotplug slot for %04x:%02x...\n",
 367                         pci_domain_nr(bus), bus->number);
 368
 369                if (rpaphp_deregister_slot(slot)) {
 370                        printk(KERN_ERR
 371                                "%s: unable to remove hotplug slot %s\n",
 372                                __func__, drc_name);
 373                        return -EIO;
 374                }
 375        }
 376
 377        /* Remove all devices below slot */
 378        pcibios_remove_pci_devices(bus);
 379
 380        /* Unmap PCI IO space */
 381        if (pcibios_unmap_io_space(bus)) {
 382                printk(KERN_ERR "%s: failed to unmap bus range\n",
 383                        __func__);
 384                return -ERANGE;
 385        }
 386
 387        /* Remove the EADS bridge device itself */
 388        BUG_ON(!bus->self);
 389        pr_debug("PCI: Now removing bridge device %s\n", pci_name(bus->self));
 390        eeh_remove_bus_device(bus->self);
 391        pci_remove_bus_device(bus->self);
 392
 393        return 0;
 394}
 395
 396/**
 397 * dlpar_remove_slot - DLPAR remove an I/O Slot
 398 * @drc_name: drc-name of newly added slot
 399 *
 400 * Remove the kernel and hotplug representations of an I/O Slot.
 401 * Return Codes:
 402 * 0                    Success
 403 * -ENODEV              Not a valid drc_name
 404 * -EINVAL              Slot already removed
 405 * -ERESTARTSYS         Signalled before obtaining lock
 406 * -EIO                 Internal Error
 407 */
 408int dlpar_remove_slot(char *drc_name)
 409{
 410        struct device_node *dn;
 411        int node_type;
 412        int rc = 0;
 413
 414        if (mutex_lock_interruptible(&rpadlpar_mutex))
 415                return -ERESTARTSYS;
 416
 417        dn = find_dlpar_node(drc_name, &node_type);
 418        if (!dn) {
 419                rc = -ENODEV;
 420                goto exit;
 421        }
 422
 423        switch (node_type) {
 424                case NODE_TYPE_VIO:
 425                        rc = dlpar_remove_vio_slot(drc_name, dn);
 426                        break;
 427                case NODE_TYPE_PHB:
 428                        rc = dlpar_remove_phb(drc_name, dn);
 429                        break;
 430                case NODE_TYPE_SLOT:
 431                        rc = dlpar_remove_pci_slot(drc_name, dn);
 432                        break;
 433        }
 434        vm_unmap_aliases();
 435
 436        printk(KERN_INFO "%s: slot %s removed\n", DLPAR_MODULE_NAME, drc_name);
 437exit:
 438        mutex_unlock(&rpadlpar_mutex);
 439        return rc;
 440}
 441
 442static inline int is_dlpar_capable(void)
 443{
 444        int rc = rtas_token("ibm,configure-connector");
 445
 446        return (int) (rc != RTAS_UNKNOWN_SERVICE);
 447}
 448
 449int __init rpadlpar_io_init(void)
 450{
 451        int rc = 0;
 452
 453        if (!is_dlpar_capable()) {
 454                printk(KERN_WARNING "%s: partition not DLPAR capable\n",
 455                        __func__);
 456                return -EPERM;
 457        }
 458
 459        rc = dlpar_sysfs_init();
 460        return rc;
 461}
 462
 463void rpadlpar_io_exit(void)
 464{
 465        dlpar_sysfs_exit();
 466        return;
 467}
 468
 469module_init(rpadlpar_io_init);
 470module_exit(rpadlpar_io_exit);
 471MODULE_LICENSE("GPL");
 472