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/module.h>
  22#include <linux/pci.h>
  23#include <linux/string.h>
  24#include <linux/vmalloc.h>
  25
  26#include <asm/pci-bridge.h>
  27#include <linux/mutex.h>
  28#include <asm/rtas.h>
  29#include <asm/vio.h>
  30
  31#include "../pci.h"
  32#include "rpaphp.h"
  33#include "rpadlpar.h"
  34
  35static DEFINE_MUTEX(rpadlpar_mutex);
  36
  37#define DLPAR_MODULE_NAME "rpadlpar_io"
  38
  39#define NODE_TYPE_VIO  1
  40#define NODE_TYPE_SLOT 2
  41#define NODE_TYPE_PHB  3
  42
  43static struct device_node *find_vio_slot_node(char *drc_name)
  44{
  45        struct device_node *parent = of_find_node_by_name(NULL, "vdevice");
  46        struct device_node *dn = NULL;
  47        char *name;
  48        int rc;
  49
  50        if (!parent)
  51                return NULL;
  52
  53        while ((dn = of_get_next_child(parent, dn))) {
  54                rc = rpaphp_get_drc_props(dn, NULL, &name, NULL, NULL);
  55                if ((rc == 0) && (!strcmp(drc_name, name)))
  56                        break;
  57        }
  58
  59        return dn;
  60}
  61
  62/* Find dlpar-capable pci node that contains the specified name and type */
  63static struct device_node *find_php_slot_pci_node(char *drc_name,
  64                                                  char *drc_type)
  65{
  66        struct device_node *np = NULL;
  67        char *name;
  68        char *type;
  69        int rc;
  70
  71        while ((np = of_find_node_by_name(np, "pci"))) {
  72                rc = rpaphp_get_drc_props(np, NULL, &name, &type, NULL);
  73                if (rc == 0)
  74                        if (!strcmp(drc_name, name) && !strcmp(drc_type, type))
  75                                break;
  76        }
  77
  78        return np;
  79}
  80
  81static struct device_node *find_dlpar_node(char *drc_name, int *node_type)
  82{
  83        struct device_node *dn;
  84
  85        dn = find_php_slot_pci_node(drc_name, "SLOT");
  86        if (dn) {
  87                *node_type = NODE_TYPE_SLOT;
  88                return dn;
  89        }
  90
  91        dn = find_php_slot_pci_node(drc_name, "PHB");
  92        if (dn) {
  93                *node_type = NODE_TYPE_PHB;
  94                return dn;
  95        }
  96
  97        dn = find_vio_slot_node(drc_name);
  98        if (dn) {
  99                *node_type = NODE_TYPE_VIO;
 100                return dn;
 101        }
 102
 103        return NULL;
 104}
 105
 106/**
 107 * find_php_slot - return hotplug slot structure for device node
 108 * @dn: target &device_node
 109 *
 110 * This routine will return the hotplug slot structure
 111 * for a given device node. Note that built-in PCI slots
 112 * may be dlpar-able, but not hot-pluggable, so this routine
 113 * will return NULL for built-in PCI slots.
 114 */
 115static struct slot *find_php_slot(struct device_node *dn)
 116{
 117        struct list_head *tmp, *n;
 118        struct slot *slot;
 119
 120        list_for_each_safe(tmp, n, &rpaphp_slot_head) {
 121                slot = list_entry(tmp, struct slot, rpaphp_slot_list);
 122                if (slot->dn == dn)
 123                        return slot;
 124        }
 125
 126        return NULL;
 127}
 128
 129static struct pci_dev *dlpar_find_new_dev(struct pci_bus *parent,
 130                                        struct device_node *dev_dn)
 131{
 132        struct pci_dev *tmp = NULL;
 133        struct device_node *child_dn;
 134
 135        list_for_each_entry(tmp, &parent->devices, bus_list) {
 136                child_dn = pci_device_to_OF_node(tmp);
 137                if (child_dn == dev_dn)
 138                        return tmp;
 139        }
 140        return NULL;
 141}
 142
 143static void dlpar_pci_add_bus(struct device_node *dn)
 144{
 145        struct pci_dn *pdn = PCI_DN(dn);
 146        struct pci_controller *phb = pdn->phb;
 147        struct pci_dev *dev = NULL;
 148
 149        eeh_add_device_tree_early(dn);
 150
 151        /* Add EADS device to PHB bus, adding new entry to bus->devices */
 152        dev = of_create_pci_dev(dn, phb->bus, pdn->devfn);
 153        if (!dev) {
 154                printk(KERN_ERR "%s: failed to create pci dev for %s\n",
 155                                __func__, dn->full_name);
 156                return;
 157        }
 158
 159        /* Scan below the new bridge */
 160        if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
 161            dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
 162                of_scan_pci_bridge(dev);
 163
 164        /* Map IO space for child bus, which may or may not succeed */
 165        pcibios_map_io_space(dev->subordinate);
 166
 167        /* Finish adding it : resource allocation, adding devices, etc...
 168         * Note that we need to perform the finish pass on the -parent-
 169         * bus of the EADS bridge so the bridge device itself gets
 170         * properly added
 171         */
 172        pcibios_finish_adding_to_bus(phb->bus);
 173}
 174
 175static int dlpar_add_pci_slot(char *drc_name, struct device_node *dn)
 176{
 177        struct pci_dev *dev;
 178        struct pci_controller *phb;
 179
 180        if (pcibios_find_pci_bus(dn))
 181                return -EINVAL;
 182
 183        /* Add pci bus */
 184        dlpar_pci_add_bus(dn);
 185
 186        /* Confirm new bridge dev was created */
 187        phb = PCI_DN(dn)->phb;
 188        dev = dlpar_find_new_dev(phb->bus, dn);
 189
 190        if (!dev) {
 191                printk(KERN_ERR "%s: unable to add bus %s\n", __func__,
 192                        drc_name);
 193                return -EIO;
 194        }
 195
 196        if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
 197                printk(KERN_ERR "%s: unexpected header type %d, unable to add bus %s\n",
 198                        __func__, dev->hdr_type, drc_name);
 199                return -EIO;
 200        }
 201
 202        /* Add hotplug slot */
 203        if (rpaphp_add_slot(dn)) {
 204                printk(KERN_ERR "%s: unable to add hotplug slot %s\n",
 205                        __func__, drc_name);
 206                return -EIO;
 207        }
 208        return 0;
 209}
 210
 211static int dlpar_remove_phb(char *drc_name, struct device_node *dn)
 212{
 213        struct slot *slot;
 214        struct pci_dn *pdn;
 215        int rc = 0;
 216
 217        if (!pcibios_find_pci_bus(dn))
 218                return -EINVAL;
 219
 220        /* If pci slot is hotplugable, use hotplug to remove it */
 221        slot = find_php_slot(dn);
 222        if (slot && rpaphp_deregister_slot(slot)) {
 223                printk(KERN_ERR "%s: unable to remove hotplug slot %s\n",
 224                       __func__, drc_name);
 225                return -EIO;
 226        }
 227
 228        pdn = dn->data;
 229        BUG_ON(!pdn || !pdn->phb);
 230        rc = remove_phb_dynamic(pdn->phb);
 231        if (rc < 0)
 232                return rc;
 233
 234        pdn->phb = NULL;
 235
 236        return 0;
 237}
 238
 239static int dlpar_add_phb(char *drc_name, struct device_node *dn)
 240{
 241        struct pci_controller *phb;
 242
 243        if (PCI_DN(dn) && PCI_DN(dn)->phb) {
 244                /* PHB already exists */
 245                return -EINVAL;
 246        }
 247
 248        phb = init_phb_dynamic(dn);
 249        if (!phb)
 250                return -EIO;
 251
 252        if (rpaphp_add_slot(dn)) {
 253                printk(KERN_ERR "%s: unable to add hotplug slot %s\n",
 254                        __func__, drc_name);
 255                return -EIO;
 256        }
 257        return 0;
 258}
 259
 260static int dlpar_add_vio_slot(char *drc_name, struct device_node *dn)
 261{
 262        if (vio_find_node(dn))
 263                return -EINVAL;
 264
 265        if (!vio_register_device_node(dn)) {
 266                printk(KERN_ERR
 267                        "%s: failed to register vio node %s\n",
 268                        __func__, drc_name);
 269                return -EIO;
 270        }
 271        return 0;
 272}
 273
 274/**
 275 * dlpar_add_slot - DLPAR add an I/O Slot
 276 * @drc_name: drc-name of newly added slot
 277 *
 278 * Make the hotplug module and the kernel aware of a newly added I/O Slot.
 279 * Return Codes:
 280 * 0                    Success
 281 * -ENODEV              Not a valid drc_name
 282 * -EINVAL              Slot already added
 283 * -ERESTARTSYS         Signalled before obtaining lock
 284 * -EIO                 Internal PCI Error
 285 */
 286int dlpar_add_slot(char *drc_name)
 287{
 288        struct device_node *dn = NULL;
 289        int node_type;
 290        int rc = -EIO;
 291
 292        if (mutex_lock_interruptible(&rpadlpar_mutex))
 293                return -ERESTARTSYS;
 294
 295        /* Find newly added node */
 296        dn = find_dlpar_node(drc_name, &node_type);
 297        if (!dn) {
 298                rc = -ENODEV;
 299                goto exit;
 300        }
 301
 302        switch (node_type) {
 303                case NODE_TYPE_VIO:
 304                        rc = dlpar_add_vio_slot(drc_name, dn);
 305                        break;
 306                case NODE_TYPE_SLOT:
 307                        rc = dlpar_add_pci_slot(drc_name, dn);
 308                        break;
 309                case NODE_TYPE_PHB:
 310                        rc = dlpar_add_phb(drc_name, dn);
 311                        break;
 312        }
 313
 314        printk(KERN_INFO "%s: slot %s added\n", DLPAR_MODULE_NAME, drc_name);
 315exit:
 316        mutex_unlock(&rpadlpar_mutex);
 317        return rc;
 318}
 319
 320/**
 321 * dlpar_remove_vio_slot - DLPAR remove a virtual I/O Slot
 322 * @drc_name: drc-name of newly added slot
 323 * @dn: &device_node
 324 *
 325 * Remove the kernel and hotplug representations of an I/O Slot.
 326 * Return Codes:
 327 * 0                    Success
 328 * -EINVAL              Vio dev doesn't exist
 329 */
 330static int dlpar_remove_vio_slot(char *drc_name, struct device_node *dn)
 331{
 332        struct vio_dev *vio_dev;
 333
 334        vio_dev = vio_find_node(dn);
 335        if (!vio_dev)
 336                return -EINVAL;
 337
 338        vio_unregister_device(vio_dev);
 339        return 0;
 340}
 341
 342/**
 343 * dlpar_remove_pci_slot - DLPAR remove a PCI I/O Slot
 344 * @drc_name: drc-name of newly added slot
 345 * @dn: &device_node
 346 *
 347 * Remove the kernel and hotplug representations of a PCI I/O Slot.
 348 * Return Codes:
 349 * 0                    Success
 350 * -ENODEV              Not a valid drc_name
 351 * -EIO                 Internal PCI Error
 352 */
 353int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn)
 354{
 355        struct pci_bus *bus;
 356        struct slot *slot;
 357
 358        bus = pcibios_find_pci_bus(dn);
 359        if (!bus)
 360                return -EINVAL;
 361
 362        pr_debug("PCI: Removing PCI slot below EADS bridge %s\n",
 363                 bus->self ? pci_name(bus->self) : "<!PHB!>");
 364
 365        slot = find_php_slot(dn);
 366        if (slot) {
 367                pr_debug("PCI: Removing hotplug slot for %04x:%02x...\n",
 368                         pci_domain_nr(bus), bus->number);
 369
 370                if (rpaphp_deregister_slot(slot)) {
 371                        printk(KERN_ERR
 372                                "%s: unable to remove hotplug slot %s\n",
 373                                __func__, drc_name);
 374                        return -EIO;
 375                }
 376        }
 377
 378        /* Remove all devices below slot */
 379        pcibios_remove_pci_devices(bus);
 380
 381        /* Unmap PCI IO space */
 382        if (pcibios_unmap_io_space(bus)) {
 383                printk(KERN_ERR "%s: failed to unmap bus range\n",
 384                        __func__);
 385                return -ERANGE;
 386        }
 387
 388        /* Remove the EADS bridge device itself */
 389        BUG_ON(!bus->self);
 390        pr_debug("PCI: Now removing bridge device %s\n", pci_name(bus->self));
 391        pci_stop_and_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