linux/drivers/pci/hotplug/pnv_php.c
<<
>>
Prefs
   1/*
   2 * PCI Hotplug Driver for PowerPC PowerNV platform.
   3 *
   4 * Copyright Gavin Shan, IBM Corporation 2016.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 */
  11
  12#include <linux/libfdt.h>
  13#include <linux/module.h>
  14#include <linux/pci.h>
  15#include <linux/pci_hotplug.h>
  16
  17#include <asm/opal.h>
  18#include <asm/pnv-pci.h>
  19#include <asm/ppc-pci.h>
  20
  21#define DRIVER_VERSION  "0.1"
  22#define DRIVER_AUTHOR   "Gavin Shan, IBM Corporation"
  23#define DRIVER_DESC     "PowerPC PowerNV PCI Hotplug Driver"
  24
  25static LIST_HEAD(pnv_php_slot_list);
  26static DEFINE_SPINLOCK(pnv_php_lock);
  27
  28static void pnv_php_register(struct device_node *dn);
  29static void pnv_php_unregister_one(struct device_node *dn);
  30static void pnv_php_unregister(struct device_node *dn);
  31
  32static void pnv_php_free_slot(struct kref *kref)
  33{
  34        struct pnv_php_slot *php_slot = container_of(kref,
  35                                        struct pnv_php_slot, kref);
  36
  37        WARN_ON(!list_empty(&php_slot->children));
  38        kfree(php_slot->name);
  39        kfree(php_slot);
  40}
  41
  42static inline void pnv_php_put_slot(struct pnv_php_slot *php_slot)
  43{
  44
  45        if (WARN_ON(!php_slot))
  46                return;
  47
  48        kref_put(&php_slot->kref, pnv_php_free_slot);
  49}
  50
  51static struct pnv_php_slot *pnv_php_match(struct device_node *dn,
  52                                          struct pnv_php_slot *php_slot)
  53{
  54        struct pnv_php_slot *target, *tmp;
  55
  56        if (php_slot->dn == dn) {
  57                kref_get(&php_slot->kref);
  58                return php_slot;
  59        }
  60
  61        list_for_each_entry(tmp, &php_slot->children, link) {
  62                target = pnv_php_match(dn, tmp);
  63                if (target)
  64                        return target;
  65        }
  66
  67        return NULL;
  68}
  69
  70struct pnv_php_slot *pnv_php_find_slot(struct device_node *dn)
  71{
  72        struct pnv_php_slot *php_slot, *tmp;
  73        unsigned long flags;
  74
  75        spin_lock_irqsave(&pnv_php_lock, flags);
  76        list_for_each_entry(tmp, &pnv_php_slot_list, link) {
  77                php_slot = pnv_php_match(dn, tmp);
  78                if (php_slot) {
  79                        spin_unlock_irqrestore(&pnv_php_lock, flags);
  80                        return php_slot;
  81                }
  82        }
  83        spin_unlock_irqrestore(&pnv_php_lock, flags);
  84
  85        return NULL;
  86}
  87EXPORT_SYMBOL_GPL(pnv_php_find_slot);
  88
  89/*
  90 * Remove pdn for all children of the indicated device node.
  91 * The function should remove pdn in a depth-first manner.
  92 */
  93static void pnv_php_rmv_pdns(struct device_node *dn)
  94{
  95        struct device_node *child;
  96
  97        for_each_child_of_node(dn, child) {
  98                pnv_php_rmv_pdns(child);
  99
 100                pci_remove_device_node_info(child);
 101        }
 102}
 103
 104/*
 105 * Detach all child nodes of the indicated device nodes. The
 106 * function should handle device nodes in depth-first manner.
 107 *
 108 * We should not invoke of_node_release() as the memory for
 109 * individual device node is part of large memory block. The
 110 * large block is allocated from memblock (system bootup) or
 111 * kmalloc() when unflattening the device tree by OF changeset.
 112 * We can not free the large block allocated from memblock. For
 113 * later case, it should be released at once.
 114 */
 115static void pnv_php_detach_device_nodes(struct device_node *parent)
 116{
 117        struct device_node *dn;
 118        int refcount;
 119
 120        for_each_child_of_node(parent, dn) {
 121                pnv_php_detach_device_nodes(dn);
 122
 123                of_node_put(dn);
 124                refcount = atomic_read(&dn->kobj.kref.refcount);
 125                if (unlikely(refcount != 1))
 126                        pr_warn("Invalid refcount %d on <%s>\n",
 127                                refcount, of_node_full_name(dn));
 128
 129                of_detach_node(dn);
 130        }
 131}
 132
 133static void pnv_php_rmv_devtree(struct pnv_php_slot *php_slot)
 134{
 135        pnv_php_rmv_pdns(php_slot->dn);
 136
 137        /*
 138         * Decrease the refcount if the device nodes were created
 139         * through OF changeset before detaching them.
 140         */
 141        if (php_slot->fdt)
 142                of_changeset_destroy(&php_slot->ocs);
 143        pnv_php_detach_device_nodes(php_slot->dn);
 144
 145        if (php_slot->fdt) {
 146                kfree(php_slot->dt);
 147                kfree(php_slot->fdt);
 148                php_slot->dt        = NULL;
 149                php_slot->dn->child = NULL;
 150                php_slot->fdt       = NULL;
 151        }
 152}
 153
 154/*
 155 * As the nodes in OF changeset are applied in reverse order, we
 156 * need revert the nodes in advance so that we have correct node
 157 * order after the changeset is applied.
 158 */
 159static void pnv_php_reverse_nodes(struct device_node *parent)
 160{
 161        struct device_node *child, *next;
 162
 163        /* In-depth first */
 164        for_each_child_of_node(parent, child)
 165                pnv_php_reverse_nodes(child);
 166
 167        /* Reverse the nodes in the child list */
 168        child = parent->child;
 169        parent->child = NULL;
 170        while (child) {
 171                next = child->sibling;
 172
 173                child->sibling = parent->child;
 174                parent->child = child;
 175                child = next;
 176        }
 177}
 178
 179static int pnv_php_populate_changeset(struct of_changeset *ocs,
 180                                      struct device_node *dn)
 181{
 182        struct device_node *child;
 183        int ret = 0;
 184
 185        for_each_child_of_node(dn, child) {
 186                ret = of_changeset_attach_node(ocs, child);
 187                if (unlikely(ret))
 188                        break;
 189
 190                ret = pnv_php_populate_changeset(ocs, child);
 191                if (unlikely(ret))
 192                        break;
 193        }
 194
 195        return ret;
 196}
 197
 198static void *pnv_php_add_one_pdn(struct device_node *dn, void *data)
 199{
 200        struct pci_controller *hose = (struct pci_controller *)data;
 201        struct pci_dn *pdn;
 202
 203        pdn = pci_add_device_node_info(hose, dn);
 204        if (unlikely(!pdn))
 205                return ERR_PTR(-ENOMEM);
 206
 207        return NULL;
 208}
 209
 210static void pnv_php_add_pdns(struct pnv_php_slot *slot)
 211{
 212        struct pci_controller *hose = pci_bus_to_host(slot->bus);
 213
 214        pci_traverse_device_nodes(slot->dn, pnv_php_add_one_pdn, hose);
 215}
 216
 217static int pnv_php_add_devtree(struct pnv_php_slot *php_slot)
 218{
 219        void *fdt, *fdt1, *dt;
 220        int ret;
 221
 222        /* We don't know the FDT blob size. We try to get it through
 223         * maximal memory chunk and then copy it to another chunk that
 224         * fits the real size.
 225         */
 226        fdt1 = kzalloc(0x10000, GFP_KERNEL);
 227        if (unlikely(!fdt1)) {
 228                ret = -ENOMEM;
 229                dev_warn(&php_slot->pdev->dev, "Cannot alloc FDT blob\n");
 230                goto out;
 231        }
 232
 233        ret = pnv_pci_get_device_tree(php_slot->dn->phandle, fdt1, 0x10000);
 234        if (unlikely(ret)) {
 235                dev_warn(&php_slot->pdev->dev, "Error %d getting FDT blob\n",
 236                         ret);
 237                goto free_fdt1;
 238        }
 239
 240        fdt = kzalloc(fdt_totalsize(fdt1), GFP_KERNEL);
 241        if (unlikely(!fdt)) {
 242                ret = -ENOMEM;
 243                dev_warn(&php_slot->pdev->dev, "Cannot %d bytes memory\n",
 244                         fdt_totalsize(fdt1));
 245                goto free_fdt1;
 246        }
 247
 248        /* Unflatten device tree blob */
 249        memcpy(fdt, fdt1, fdt_totalsize(fdt1));
 250        dt = of_fdt_unflatten_tree(fdt, php_slot->dn, NULL);
 251        if (unlikely(!dt)) {
 252                ret = -EINVAL;
 253                dev_warn(&php_slot->pdev->dev, "Cannot unflatten FDT\n");
 254                goto free_fdt;
 255        }
 256
 257        /* Initialize and apply the changeset */
 258        of_changeset_init(&php_slot->ocs);
 259        pnv_php_reverse_nodes(php_slot->dn);
 260        ret = pnv_php_populate_changeset(&php_slot->ocs, php_slot->dn);
 261        if (unlikely(ret)) {
 262                pnv_php_reverse_nodes(php_slot->dn);
 263                dev_warn(&php_slot->pdev->dev, "Error %d populating changeset\n",
 264                         ret);
 265                goto free_dt;
 266        }
 267
 268        php_slot->dn->child = NULL;
 269        ret = of_changeset_apply(&php_slot->ocs);
 270        if (unlikely(ret)) {
 271                dev_warn(&php_slot->pdev->dev, "Error %d applying changeset\n",
 272                         ret);
 273                goto destroy_changeset;
 274        }
 275
 276        /* Add device node firmware data */
 277        pnv_php_add_pdns(php_slot);
 278        php_slot->fdt = fdt;
 279        php_slot->dt  = dt;
 280        kfree(fdt1);
 281        goto out;
 282
 283destroy_changeset:
 284        of_changeset_destroy(&php_slot->ocs);
 285free_dt:
 286        kfree(dt);
 287        php_slot->dn->child = NULL;
 288free_fdt:
 289        kfree(fdt);
 290free_fdt1:
 291        kfree(fdt1);
 292out:
 293        return ret;
 294}
 295
 296int pnv_php_set_slot_power_state(struct hotplug_slot *slot,
 297                                 uint8_t state)
 298{
 299        struct pnv_php_slot *php_slot = slot->private;
 300        struct opal_msg msg;
 301        int ret;
 302
 303        ret = pnv_pci_set_power_state(php_slot->id, state, &msg);
 304        if (likely(ret > 0)) {
 305                if (be64_to_cpu(msg.params[1]) != php_slot->dn->phandle ||
 306                    be64_to_cpu(msg.params[2]) != state                 ||
 307                    be64_to_cpu(msg.params[3]) != OPAL_SUCCESS) {
 308                        dev_warn(&php_slot->pdev->dev, "Wrong msg (%lld, %lld, %lld)\n",
 309                                 be64_to_cpu(msg.params[1]),
 310                                 be64_to_cpu(msg.params[2]),
 311                                 be64_to_cpu(msg.params[3]));
 312                        return -ENOMSG;
 313                }
 314        } else if (unlikely(ret < 0)) {
 315                dev_warn(&php_slot->pdev->dev, "Error %d powering %s\n",
 316                         ret, (state == OPAL_PCI_SLOT_POWER_ON) ? "on" : "off");
 317                return ret;
 318        }
 319
 320        if (state == OPAL_PCI_SLOT_POWER_OFF || state == OPAL_PCI_SLOT_OFFLINE)
 321                pnv_php_rmv_devtree(php_slot);
 322        else
 323                ret = pnv_php_add_devtree(php_slot);
 324
 325        return ret;
 326}
 327EXPORT_SYMBOL_GPL(pnv_php_set_slot_power_state);
 328
 329static int pnv_php_get_power_state(struct hotplug_slot *slot, u8 *state)
 330{
 331        struct pnv_php_slot *php_slot = slot->private;
 332        uint8_t power_state = OPAL_PCI_SLOT_POWER_ON;
 333        int ret;
 334
 335        /*
 336         * Retrieve power status from firmware. If we fail
 337         * getting that, the power status fails back to
 338         * be on.
 339         */
 340        ret = pnv_pci_get_power_state(php_slot->id, &power_state);
 341        if (unlikely(ret)) {
 342                dev_warn(&php_slot->pdev->dev, "Error %d getting power status\n",
 343                         ret);
 344        } else {
 345                *state = power_state;
 346                slot->info->power_status = power_state;
 347        }
 348
 349        return 0;
 350}
 351
 352static int pnv_php_get_adapter_state(struct hotplug_slot *slot, u8 *state)
 353{
 354        struct pnv_php_slot *php_slot = slot->private;
 355        uint8_t presence = OPAL_PCI_SLOT_EMPTY;
 356        int ret;
 357
 358        /*
 359         * Retrieve presence status from firmware. If we can't
 360         * get that, it will fail back to be empty.
 361         */
 362        ret = pnv_pci_get_presence_state(php_slot->id, &presence);
 363        if (likely(ret >= 0)) {
 364                *state = presence;
 365                slot->info->adapter_status = presence;
 366                ret = 0;
 367        } else {
 368                dev_warn(&php_slot->pdev->dev, "Error %d getting presence\n",
 369                         ret);
 370        }
 371
 372        return ret;
 373}
 374
 375static int pnv_php_set_attention_state(struct hotplug_slot *slot, u8 state)
 376{
 377        /* FIXME: Make it real once firmware supports it */
 378        slot->info->attention_status = state;
 379
 380        return 0;
 381}
 382
 383static int pnv_php_enable(struct pnv_php_slot *php_slot, bool rescan)
 384{
 385        struct hotplug_slot *slot = &php_slot->slot;
 386        uint8_t presence = OPAL_PCI_SLOT_EMPTY;
 387        uint8_t power_status = OPAL_PCI_SLOT_POWER_ON;
 388        int ret;
 389
 390        /* Check if the slot has been configured */
 391        if (php_slot->state != PNV_PHP_STATE_REGISTERED)
 392                return 0;
 393
 394        /* Retrieve slot presence status */
 395        ret = pnv_php_get_adapter_state(slot, &presence);
 396        if (unlikely(ret))
 397                return ret;
 398
 399        /* Proceed if there have nothing behind the slot */
 400        if (presence == OPAL_PCI_SLOT_EMPTY)
 401                goto scan;
 402
 403        /*
 404         * If the power supply to the slot is off, we can't detect
 405         * adapter presence state. That means we have to turn the
 406         * slot on before going to probe slot's presence state.
 407         *
 408         * On the first time, we don't change the power status to
 409         * boost system boot with assumption that the firmware
 410         * supplies consistent slot power status: empty slot always
 411         * has its power off and non-empty slot has its power on.
 412         */
 413        if (!php_slot->power_state_check) {
 414                php_slot->power_state_check = true;
 415
 416                ret = pnv_php_get_power_state(slot, &power_status);
 417                if (unlikely(ret))
 418                        return ret;
 419
 420                if (power_status != OPAL_PCI_SLOT_POWER_ON)
 421                        return 0;
 422        }
 423
 424        /* Check the power status. Scan the slot if it is already on */
 425        ret = pnv_php_get_power_state(slot, &power_status);
 426        if (unlikely(ret))
 427                return ret;
 428
 429        if (power_status == OPAL_PCI_SLOT_POWER_ON)
 430                goto scan;
 431
 432        /* Power is off, turn it on and then scan the slot */
 433        ret = pnv_php_set_slot_power_state(slot, OPAL_PCI_SLOT_POWER_ON);
 434        if (unlikely(ret))
 435                return ret;
 436
 437scan:
 438        if (presence == OPAL_PCI_SLOT_PRESENT) {
 439                if (rescan) {
 440                        pci_lock_rescan_remove();
 441                        pci_hp_add_devices(php_slot->bus);
 442                        pci_unlock_rescan_remove();
 443                }
 444
 445                /* Rescan for child hotpluggable slots */
 446                php_slot->state = PNV_PHP_STATE_POPULATED;
 447                if (rescan)
 448                        pnv_php_register(php_slot->dn);
 449        } else {
 450                php_slot->state = PNV_PHP_STATE_POPULATED;
 451        }
 452
 453        return 0;
 454}
 455
 456static int pnv_php_enable_slot(struct hotplug_slot *slot)
 457{
 458        struct pnv_php_slot *php_slot = container_of(slot,
 459                                                     struct pnv_php_slot, slot);
 460
 461        return pnv_php_enable(php_slot, true);
 462}
 463
 464static int pnv_php_disable_slot(struct hotplug_slot *slot)
 465{
 466        struct pnv_php_slot *php_slot = slot->private;
 467        int ret;
 468
 469        if (php_slot->state != PNV_PHP_STATE_POPULATED)
 470                return 0;
 471
 472        /* Remove all devices behind the slot */
 473        pci_lock_rescan_remove();
 474        pci_hp_remove_devices(php_slot->bus);
 475        pci_unlock_rescan_remove();
 476
 477        /* Detach the child hotpluggable slots */
 478        pnv_php_unregister(php_slot->dn);
 479
 480        /* Notify firmware and remove device nodes */
 481        ret = pnv_php_set_slot_power_state(slot, OPAL_PCI_SLOT_POWER_OFF);
 482
 483        php_slot->state = PNV_PHP_STATE_REGISTERED;
 484        return ret;
 485}
 486
 487static struct hotplug_slot_ops php_slot_ops = {
 488        .get_power_status       = pnv_php_get_power_state,
 489        .get_adapter_status     = pnv_php_get_adapter_state,
 490        .set_attention_status   = pnv_php_set_attention_state,
 491        .enable_slot            = pnv_php_enable_slot,
 492        .disable_slot           = pnv_php_disable_slot,
 493};
 494
 495static void pnv_php_release(struct hotplug_slot *slot)
 496{
 497        struct pnv_php_slot *php_slot = slot->private;
 498        unsigned long flags;
 499
 500        /* Remove from global or child list */
 501        spin_lock_irqsave(&pnv_php_lock, flags);
 502        list_del(&php_slot->link);
 503        spin_unlock_irqrestore(&pnv_php_lock, flags);
 504
 505        /* Detach from parent */
 506        pnv_php_put_slot(php_slot);
 507        pnv_php_put_slot(php_slot->parent);
 508}
 509
 510static struct pnv_php_slot *pnv_php_alloc_slot(struct device_node *dn)
 511{
 512        struct pnv_php_slot *php_slot;
 513        struct pci_bus *bus;
 514        const char *label;
 515        uint64_t id;
 516
 517        label = of_get_property(dn, "ibm,slot-label", NULL);
 518        if (unlikely(!label))
 519                return NULL;
 520
 521        if (pnv_pci_get_slot_id(dn, &id))
 522                return NULL;
 523
 524        bus = pci_find_bus_by_node(dn);
 525        if (unlikely(!bus))
 526                return NULL;
 527
 528        php_slot = kzalloc(sizeof(*php_slot), GFP_KERNEL);
 529        if (unlikely(!php_slot))
 530                return NULL;
 531
 532        php_slot->name = kstrdup(label, GFP_KERNEL);
 533        if (unlikely(!php_slot->name)) {
 534                kfree(php_slot);
 535                return NULL;
 536        }
 537
 538        if (likely(dn->child && PCI_DN(dn->child)))
 539                php_slot->slot_no = PCI_SLOT(PCI_DN(dn->child)->devfn);
 540        else
 541                php_slot->slot_no = -1;   /* Placeholder slot */
 542
 543        kref_init(&php_slot->kref);
 544        php_slot->state                 = PNV_PHP_STATE_INITIALIZED;
 545        php_slot->dn                    = dn;
 546        php_slot->pdev                  = bus->self;
 547        php_slot->bus                   = bus;
 548        php_slot->id                    = id;
 549        php_slot->power_state_check     = false;
 550        php_slot->slot.ops              = &php_slot_ops;
 551        php_slot->slot.info             = &php_slot->slot_info;
 552        php_slot->slot.release          = pnv_php_release;
 553        php_slot->slot.private          = php_slot;
 554
 555        INIT_LIST_HEAD(&php_slot->children);
 556        INIT_LIST_HEAD(&php_slot->link);
 557
 558        return php_slot;
 559}
 560
 561static int pnv_php_register_slot(struct pnv_php_slot *php_slot)
 562{
 563        struct pnv_php_slot *parent;
 564        struct device_node *dn = php_slot->dn;
 565        unsigned long flags;
 566        int ret;
 567
 568        /* Check if the slot is registered or not */
 569        parent = pnv_php_find_slot(php_slot->dn);
 570        if (unlikely(parent)) {
 571                pnv_php_put_slot(parent);
 572                return -EEXIST;
 573        }
 574
 575        /* Register PCI slot */
 576        ret = pci_hp_register(&php_slot->slot, php_slot->bus,
 577                              php_slot->slot_no, php_slot->name);
 578        if (unlikely(ret)) {
 579                dev_warn(&php_slot->pdev->dev, "Error %d registering slot\n",
 580                         ret);
 581                return ret;
 582        }
 583
 584        /* Attach to the parent's child list or global list */
 585        while ((dn = of_get_parent(dn))) {
 586                if (!PCI_DN(dn)) {
 587                        of_node_put(dn);
 588                        break;
 589                }
 590
 591                parent = pnv_php_find_slot(dn);
 592                if (parent) {
 593                        of_node_put(dn);
 594                        break;
 595                }
 596
 597                of_node_put(dn);
 598        }
 599
 600        spin_lock_irqsave(&pnv_php_lock, flags);
 601        php_slot->parent = parent;
 602        if (parent)
 603                list_add_tail(&php_slot->link, &parent->children);
 604        else
 605                list_add_tail(&php_slot->link, &pnv_php_slot_list);
 606        spin_unlock_irqrestore(&pnv_php_lock, flags);
 607
 608        php_slot->state = PNV_PHP_STATE_REGISTERED;
 609        return 0;
 610}
 611
 612static int pnv_php_register_one(struct device_node *dn)
 613{
 614        struct pnv_php_slot *php_slot;
 615        const __be32 *prop32;
 616        int ret;
 617
 618        /* Check if it's hotpluggable slot */
 619        prop32 = of_get_property(dn, "ibm,slot-pluggable", NULL);
 620        if (!prop32 || !of_read_number(prop32, 1))
 621                return -ENXIO;
 622
 623        prop32 = of_get_property(dn, "ibm,reset-by-firmware", NULL);
 624        if (!prop32 || !of_read_number(prop32, 1))
 625                return -ENXIO;
 626
 627        php_slot = pnv_php_alloc_slot(dn);
 628        if (unlikely(!php_slot))
 629                return -ENODEV;
 630
 631        ret = pnv_php_register_slot(php_slot);
 632        if (unlikely(ret))
 633                goto free_slot;
 634
 635        ret = pnv_php_enable(php_slot, false);
 636        if (unlikely(ret))
 637                goto unregister_slot;
 638
 639        return 0;
 640
 641unregister_slot:
 642        pnv_php_unregister_one(php_slot->dn);
 643free_slot:
 644        pnv_php_put_slot(php_slot);
 645        return ret;
 646}
 647
 648static void pnv_php_register(struct device_node *dn)
 649{
 650        struct device_node *child;
 651
 652        /*
 653         * The parent slots should be registered before their
 654         * child slots.
 655         */
 656        for_each_child_of_node(dn, child) {
 657                pnv_php_register_one(child);
 658                pnv_php_register(child);
 659        }
 660}
 661
 662static void pnv_php_unregister_one(struct device_node *dn)
 663{
 664        struct pnv_php_slot *php_slot;
 665
 666        php_slot = pnv_php_find_slot(dn);
 667        if (!php_slot)
 668                return;
 669
 670        php_slot->state = PNV_PHP_STATE_OFFLINE;
 671        pnv_php_put_slot(php_slot);
 672        pci_hp_deregister(&php_slot->slot);
 673}
 674
 675static void pnv_php_unregister(struct device_node *dn)
 676{
 677        struct device_node *child;
 678
 679        /* The child slots should go before their parent slots */
 680        for_each_child_of_node(dn, child) {
 681                pnv_php_unregister(child);
 682                pnv_php_unregister_one(child);
 683        }
 684}
 685
 686static int __init pnv_php_init(void)
 687{
 688        struct device_node *dn;
 689
 690        pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
 691        for_each_compatible_node(dn, NULL, "ibm,ioda2-phb")
 692                pnv_php_register(dn);
 693
 694        return 0;
 695}
 696
 697static void __exit pnv_php_exit(void)
 698{
 699        struct device_node *dn;
 700
 701        for_each_compatible_node(dn, NULL, "ibm,ioda2-phb")
 702                pnv_php_unregister(dn);
 703}
 704
 705module_init(pnv_php_init);
 706module_exit(pnv_php_exit);
 707
 708MODULE_VERSION(DRIVER_VERSION);
 709MODULE_LICENSE("GPL v2");
 710MODULE_AUTHOR(DRIVER_AUTHOR);
 711MODULE_DESCRIPTION(DRIVER_DESC);
 712