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 slot *slot, *next;
 118
 119        list_for_each_entry_safe(slot, next, &rpaphp_slot_head,
 120                                 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(pdn);
 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 %pOF\n",
 154                                __func__, dn);
 155                return;
 156        }
 157
 158        /* Scan below the new bridge */
 159        if (pci_is_bridge(dev))
 160                of_scan_pci_bridge(dev);
 161
 162        /* Map IO space for child bus, which may or may not succeed */
 163        pcibios_map_io_space(dev->subordinate);
 164
 165        /* Finish adding it : resource allocation, adding devices, etc...
 166         * Note that we need to perform the finish pass on the -parent-
 167         * bus of the EADS bridge so the bridge device itself gets
 168         * properly added
 169         */
 170        pcibios_finish_adding_to_bus(phb->bus);
 171}
 172
 173static int dlpar_add_pci_slot(char *drc_name, struct device_node *dn)
 174{
 175        struct pci_dev *dev;
 176        struct pci_controller *phb;
 177
 178        if (pci_find_bus_by_node(dn))
 179                return -EINVAL;
 180
 181        /* Add pci bus */
 182        dlpar_pci_add_bus(dn);
 183
 184        /* Confirm new bridge dev was created */
 185        phb = PCI_DN(dn)->phb;
 186        dev = dlpar_find_new_dev(phb->bus, dn);
 187
 188        if (!dev) {
 189                printk(KERN_ERR "%s: unable to add bus %s\n", __func__,
 190                        drc_name);
 191                return -EIO;
 192        }
 193
 194        if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
 195                printk(KERN_ERR "%s: unexpected header type %d, unable to add bus %s\n",
 196                        __func__, dev->hdr_type, drc_name);
 197                return -EIO;
 198        }
 199
 200        /* Add hotplug slot */
 201        if (rpaphp_add_slot(dn)) {
 202                printk(KERN_ERR "%s: unable to add hotplug slot %s\n",
 203                        __func__, drc_name);
 204                return -EIO;
 205        }
 206        return 0;
 207}
 208
 209static int dlpar_remove_phb(char *drc_name, struct device_node *dn)
 210{
 211        struct slot *slot;
 212        struct pci_dn *pdn;
 213        int rc = 0;
 214
 215        if (!pci_find_bus_by_node(dn))
 216                return -EINVAL;
 217
 218        /* If pci slot is hotpluggable, use hotplug to remove it */
 219        slot = find_php_slot(dn);
 220        if (slot && rpaphp_deregister_slot(slot)) {
 221                printk(KERN_ERR "%s: unable to remove hotplug slot %s\n",
 222                       __func__, drc_name);
 223                return -EIO;
 224        }
 225
 226        pdn = dn->data;
 227        BUG_ON(!pdn || !pdn->phb);
 228        rc = remove_phb_dynamic(pdn->phb);
 229        if (rc < 0)
 230                return rc;
 231
 232        pdn->phb = NULL;
 233
 234        return 0;
 235}
 236
 237static int dlpar_add_phb(char *drc_name, struct device_node *dn)
 238{
 239        struct pci_controller *phb;
 240
 241        if (PCI_DN(dn) && PCI_DN(dn)->phb) {
 242                /* PHB already exists */
 243                return -EINVAL;
 244        }
 245
 246        phb = init_phb_dynamic(dn);
 247        if (!phb)
 248                return -EIO;
 249
 250        if (rpaphp_add_slot(dn)) {
 251                printk(KERN_ERR "%s: unable to add hotplug slot %s\n",
 252                        __func__, drc_name);
 253                return -EIO;
 254        }
 255        return 0;
 256}
 257
 258static int dlpar_add_vio_slot(char *drc_name, struct device_node *dn)
 259{
 260        struct vio_dev *vio_dev;
 261
 262        vio_dev = vio_find_node(dn);
 263        if (vio_dev) {
 264                put_device(&vio_dev->dev);
 265                return -EINVAL;
 266        }
 267
 268        if (!vio_register_device_node(dn)) {
 269                printk(KERN_ERR
 270                        "%s: failed to register vio node %s\n",
 271                        __func__, drc_name);
 272                return -EIO;
 273        }
 274        return 0;
 275}
 276
 277/**
 278 * dlpar_add_slot - DLPAR add an I/O Slot
 279 * @drc_name: drc-name of newly added slot
 280 *
 281 * Make the hotplug module and the kernel aware of a newly added I/O Slot.
 282 * Return Codes:
 283 * 0                    Success
 284 * -ENODEV              Not a valid drc_name
 285 * -EINVAL              Slot already added
 286 * -ERESTARTSYS         Signalled before obtaining lock
 287 * -EIO                 Internal PCI Error
 288 */
 289int dlpar_add_slot(char *drc_name)
 290{
 291        struct device_node *dn = NULL;
 292        int node_type;
 293        int rc = -EIO;
 294
 295        if (mutex_lock_interruptible(&rpadlpar_mutex))
 296                return -ERESTARTSYS;
 297
 298        /* Find newly added node */
 299        dn = find_dlpar_node(drc_name, &node_type);
 300        if (!dn) {
 301                rc = -ENODEV;
 302                goto exit;
 303        }
 304
 305        switch (node_type) {
 306                case NODE_TYPE_VIO:
 307                        rc = dlpar_add_vio_slot(drc_name, dn);
 308                        break;
 309                case NODE_TYPE_SLOT:
 310                        rc = dlpar_add_pci_slot(drc_name, dn);
 311                        break;
 312                case NODE_TYPE_PHB:
 313                        rc = dlpar_add_phb(drc_name, dn);
 314                        break;
 315        }
 316
 317        printk(KERN_INFO "%s: slot %s added\n", DLPAR_MODULE_NAME, drc_name);
 318exit:
 319        mutex_unlock(&rpadlpar_mutex);
 320        return rc;
 321}
 322
 323/**
 324 * dlpar_remove_vio_slot - DLPAR remove a virtual I/O Slot
 325 * @drc_name: drc-name of newly added slot
 326 * @dn: &device_node
 327 *
 328 * Remove the kernel and hotplug representations of an I/O Slot.
 329 * Return Codes:
 330 * 0                    Success
 331 * -EINVAL              Vio dev doesn't exist
 332 */
 333static int dlpar_remove_vio_slot(char *drc_name, struct device_node *dn)
 334{
 335        struct vio_dev *vio_dev;
 336
 337        vio_dev = vio_find_node(dn);
 338        if (!vio_dev)
 339                return -EINVAL;
 340
 341        vio_unregister_device(vio_dev);
 342
 343        put_device(&vio_dev->dev);
 344
 345        return 0;
 346}
 347
 348/**
 349 * dlpar_remove_pci_slot - DLPAR remove a PCI I/O Slot
 350 * @drc_name: drc-name of newly added slot
 351 * @dn: &device_node
 352 *
 353 * Remove the kernel and hotplug representations of a PCI I/O Slot.
 354 * Return Codes:
 355 * 0                    Success
 356 * -ENODEV              Not a valid drc_name
 357 * -EIO                 Internal PCI Error
 358 */
 359int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn)
 360{
 361        struct pci_bus *bus;
 362        struct slot *slot;
 363        int ret = 0;
 364
 365        pci_lock_rescan_remove();
 366
 367        bus = pci_find_bus_by_node(dn);
 368        if (!bus) {
 369                ret = -EINVAL;
 370                goto out;
 371        }
 372
 373        pr_debug("PCI: Removing PCI slot below EADS bridge %s\n",
 374                 bus->self ? pci_name(bus->self) : "<!PHB!>");
 375
 376        slot = find_php_slot(dn);
 377        if (slot) {
 378                pr_debug("PCI: Removing hotplug slot for %04x:%02x...\n",
 379                         pci_domain_nr(bus), bus->number);
 380
 381                if (rpaphp_deregister_slot(slot)) {
 382                        printk(KERN_ERR
 383                                "%s: unable to remove hotplug slot %s\n",
 384                                __func__, drc_name);
 385                        ret = -EIO;
 386                        goto out;
 387                }
 388        }
 389
 390        /* Remove all devices below slot */
 391        pci_hp_remove_devices(bus);
 392
 393        /* Unmap PCI IO space */
 394        if (pcibios_unmap_io_space(bus)) {
 395                printk(KERN_ERR "%s: failed to unmap bus range\n",
 396                        __func__);
 397                ret = -ERANGE;
 398                goto out;
 399        }
 400
 401        /* Remove the EADS bridge device itself */
 402        BUG_ON(!bus->self);
 403        pr_debug("PCI: Now removing bridge device %s\n", pci_name(bus->self));
 404        pci_stop_and_remove_bus_device(bus->self);
 405
 406 out:
 407        pci_unlock_rescan_remove();
 408        return ret;
 409}
 410
 411/**
 412 * dlpar_remove_slot - DLPAR remove an I/O Slot
 413 * @drc_name: drc-name of newly added slot
 414 *
 415 * Remove the kernel and hotplug representations of an I/O Slot.
 416 * Return Codes:
 417 * 0                    Success
 418 * -ENODEV              Not a valid drc_name
 419 * -EINVAL              Slot already removed
 420 * -ERESTARTSYS         Signalled before obtaining lock
 421 * -EIO                 Internal Error
 422 */
 423int dlpar_remove_slot(char *drc_name)
 424{
 425        struct device_node *dn;
 426        int node_type;
 427        int rc = 0;
 428
 429        if (mutex_lock_interruptible(&rpadlpar_mutex))
 430                return -ERESTARTSYS;
 431
 432        dn = find_dlpar_node(drc_name, &node_type);
 433        if (!dn) {
 434                rc = -ENODEV;
 435                goto exit;
 436        }
 437
 438        switch (node_type) {
 439                case NODE_TYPE_VIO:
 440                        rc = dlpar_remove_vio_slot(drc_name, dn);
 441                        break;
 442                case NODE_TYPE_PHB:
 443                        rc = dlpar_remove_phb(drc_name, dn);
 444                        break;
 445                case NODE_TYPE_SLOT:
 446                        rc = dlpar_remove_pci_slot(drc_name, dn);
 447                        break;
 448        }
 449        vm_unmap_aliases();
 450
 451        printk(KERN_INFO "%s: slot %s removed\n", DLPAR_MODULE_NAME, drc_name);
 452exit:
 453        mutex_unlock(&rpadlpar_mutex);
 454        return rc;
 455}
 456
 457static inline int is_dlpar_capable(void)
 458{
 459        int rc = rtas_token("ibm,configure-connector");
 460
 461        return (int) (rc != RTAS_UNKNOWN_SERVICE);
 462}
 463
 464int __init rpadlpar_io_init(void)
 465{
 466
 467        if (!is_dlpar_capable()) {
 468                printk(KERN_WARNING "%s: partition not DLPAR capable\n",
 469                        __func__);
 470                return -EPERM;
 471        }
 472
 473        return dlpar_sysfs_init();
 474}
 475
 476void rpadlpar_io_exit(void)
 477{
 478        dlpar_sysfs_exit();
 479        return;
 480}
 481
 482module_init(rpadlpar_io_init);
 483module_exit(rpadlpar_io_exit);
 484MODULE_LICENSE("GPL");
 485