linux/arch/powerpc/platforms/iseries/vio.c
<<
>>
Prefs
   1/*
   2 * Legacy iSeries specific vio initialisation
   3 * that needs to be built in (not a module).
   4 *
   5 * © Copyright 2007 IBM Corporation
   6 *      Author: Stephen Rothwell
   7 *      Some parts collected from various other files
   8 *
   9 * This program is free software;  you can redistribute it and/or
  10 * modify it under the terms of the GNU General Public License as
  11 * published by the Free Software Foundation; either version 2 of the
  12 * License, or (at your option) any later version.
  13 *
  14 * This program is distributed in the hope that it will be useful, but
  15 * WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * General Public License for more details.
  18 *
  19 * You should have received a copy of the GNU General Public License
  20 * along with this program; if not, write to the Free Software Foundation,
  21 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  22 */
  23#include <linux/of.h>
  24#include <linux/init.h>
  25#include <linux/slab.h>
  26#include <linux/completion.h>
  27#include <linux/proc_fs.h>
  28#include <linux/module.h>
  29
  30#include <asm/firmware.h>
  31#include <asm/vio.h>
  32#include <asm/iseries/vio.h>
  33#include <asm/iseries/iommu.h>
  34#include <asm/iseries/hv_types.h>
  35#include <asm/iseries/hv_lp_event.h>
  36
  37#define FIRST_VTY       0
  38#define NUM_VTYS        1
  39#define FIRST_VSCSI     (FIRST_VTY + NUM_VTYS)
  40#define NUM_VSCSIS      1
  41#define FIRST_VLAN      (FIRST_VSCSI + NUM_VSCSIS)
  42#define NUM_VLANS       HVMAXARCHITECTEDVIRTUALLANS
  43#define FIRST_VIODASD   (FIRST_VLAN + NUM_VLANS)
  44#define NUM_VIODASDS    HVMAXARCHITECTEDVIRTUALDISKS
  45#define FIRST_VIOCD     (FIRST_VIODASD + NUM_VIODASDS)
  46#define NUM_VIOCDS      HVMAXARCHITECTEDVIRTUALCDROMS
  47#define FIRST_VIOTAPE   (FIRST_VIOCD + NUM_VIOCDS)
  48#define NUM_VIOTAPES    HVMAXARCHITECTEDVIRTUALTAPES
  49
  50struct vio_waitevent {
  51        struct completion       com;
  52        int                     rc;
  53        u16                     sub_result;
  54};
  55
  56struct vio_resource {
  57        char    rsrcname[10];
  58        char    type[4];
  59        char    model[3];
  60};
  61
  62static struct property *new_property(const char *name, int length,
  63                const void *value)
  64{
  65        struct property *np = kzalloc(sizeof(*np) + strlen(name) + 1 + length,
  66                        GFP_KERNEL);
  67
  68        if (!np)
  69                return NULL;
  70        np->name = (char *)(np + 1);
  71        np->value = np->name + strlen(name) + 1;
  72        strcpy(np->name, name);
  73        memcpy(np->value, value, length);
  74        np->length = length;
  75        return np;
  76}
  77
  78static void free_property(struct property *np)
  79{
  80        kfree(np);
  81}
  82
  83static struct device_node *new_node(const char *path,
  84                struct device_node *parent)
  85{
  86        struct device_node *np = kzalloc(sizeof(*np), GFP_KERNEL);
  87
  88        if (!np)
  89                return NULL;
  90        np->full_name = kstrdup(path, GFP_KERNEL);
  91        if (!np->full_name) {
  92                kfree(np);
  93                return NULL;
  94        }
  95        of_node_set_flag(np, OF_DYNAMIC);
  96        kref_init(&np->kref);
  97        np->parent = of_node_get(parent);
  98        return np;
  99}
 100
 101static void free_node(struct device_node *np)
 102{
 103        struct property *next;
 104        struct property *prop;
 105
 106        next = np->properties;
 107        while (next) {
 108                prop = next;
 109                next = prop->next;
 110                free_property(prop);
 111        }
 112        of_node_put(np->parent);
 113        kfree(np->full_name);
 114        kfree(np);
 115}
 116
 117static int add_string_property(struct device_node *np, const char *name,
 118                const char *value)
 119{
 120        struct property *nprop = new_property(name, strlen(value) + 1, value);
 121
 122        if (!nprop)
 123                return 0;
 124        prom_add_property(np, nprop);
 125        return 1;
 126}
 127
 128static int add_raw_property(struct device_node *np, const char *name,
 129                int length, const void *value)
 130{
 131        struct property *nprop = new_property(name, length, value);
 132
 133        if (!nprop)
 134                return 0;
 135        prom_add_property(np, nprop);
 136        return 1;
 137}
 138
 139static struct device_node *do_device_node(struct device_node *parent,
 140                const char *name, u32 reg, u32 unit, const char *type,
 141                const char *compat, struct vio_resource *res)
 142{
 143        struct device_node *np;
 144        char path[32];
 145
 146        snprintf(path, sizeof(path), "/vdevice/%s@%08x", name, reg);
 147        np = new_node(path, parent);
 148        if (!np)
 149                return NULL;
 150        if (!add_string_property(np, "name", name) ||
 151                !add_string_property(np, "device_type", type) ||
 152                !add_string_property(np, "compatible", compat) ||
 153                !add_raw_property(np, "reg", sizeof(reg), &reg) ||
 154                !add_raw_property(np, "linux,unit_address",
 155                        sizeof(unit), &unit)) {
 156                goto node_free;
 157        }
 158        if (res) {
 159                if (!add_raw_property(np, "linux,vio_rsrcname",
 160                                sizeof(res->rsrcname), res->rsrcname) ||
 161                        !add_raw_property(np, "linux,vio_type",
 162                                sizeof(res->type), res->type) ||
 163                        !add_raw_property(np, "linux,vio_model",
 164                                sizeof(res->model), res->model))
 165                        goto node_free;
 166        }
 167        np->name = of_get_property(np, "name", NULL);
 168        np->type = of_get_property(np, "device_type", NULL);
 169        of_attach_node(np);
 170#ifdef CONFIG_PROC_DEVICETREE
 171        if (parent->pde) {
 172                struct proc_dir_entry *ent;
 173
 174                ent = proc_mkdir(strrchr(np->full_name, '/') + 1, parent->pde);
 175                if (ent)
 176                        proc_device_tree_add_node(np, ent);
 177        }
 178#endif
 179        return np;
 180
 181 node_free:
 182        free_node(np);
 183        return NULL;
 184}
 185
 186/*
 187 * This is here so that we can dynamically add viodasd
 188 * devices without exposing all the above infrastructure.
 189 */
 190struct vio_dev *vio_create_viodasd(u32 unit)
 191{
 192        struct device_node *vio_root;
 193        struct device_node *np;
 194        struct vio_dev *vdev = NULL;
 195
 196        vio_root = of_find_node_by_path("/vdevice");
 197        if (!vio_root)
 198                return NULL;
 199        np = do_device_node(vio_root, "viodasd", FIRST_VIODASD + unit, unit,
 200                        "block", "IBM,iSeries-viodasd", NULL);
 201        of_node_put(vio_root);
 202        if (np) {
 203                vdev = vio_register_device_node(np);
 204                if (!vdev)
 205                        free_node(np);
 206        }
 207        return vdev;
 208}
 209EXPORT_SYMBOL_GPL(vio_create_viodasd);
 210
 211static void __init handle_block_event(struct HvLpEvent *event)
 212{
 213        struct vioblocklpevent *bevent = (struct vioblocklpevent *)event;
 214        struct vio_waitevent *pwe;
 215
 216        if (event == NULL)
 217                /* Notification that a partition went away! */
 218                return;
 219        /* First, we should NEVER get an int here...only acks */
 220        if (hvlpevent_is_int(event)) {
 221                printk(KERN_WARNING "handle_viod_request: "
 222                       "Yikes! got an int in viodasd event handler!\n");
 223                if (hvlpevent_need_ack(event)) {
 224                        event->xRc = HvLpEvent_Rc_InvalidSubtype;
 225                        HvCallEvent_ackLpEvent(event);
 226                }
 227                return;
 228        }
 229
 230        switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
 231        case vioblockopen:
 232                /*
 233                 * Handle a response to an open request.  We get all the
 234                 * disk information in the response, so update it.  The
 235                 * correlation token contains a pointer to a waitevent
 236                 * structure that has a completion in it.  update the
 237                 * return code in the waitevent structure and post the
 238                 * completion to wake up the guy who sent the request
 239                 */
 240                pwe = (struct vio_waitevent *)event->xCorrelationToken;
 241                pwe->rc = event->xRc;
 242                pwe->sub_result = bevent->sub_result;
 243                complete(&pwe->com);
 244                break;
 245        case vioblockclose:
 246                break;
 247        default:
 248                printk(KERN_WARNING "handle_viod_request: unexpected subtype!");
 249                if (hvlpevent_need_ack(event)) {
 250                        event->xRc = HvLpEvent_Rc_InvalidSubtype;
 251                        HvCallEvent_ackLpEvent(event);
 252                }
 253        }
 254}
 255
 256static void __init probe_disk(struct device_node *vio_root, u32 unit)
 257{
 258        HvLpEvent_Rc hvrc;
 259        struct vio_waitevent we;
 260        u16 flags = 0;
 261
 262retry:
 263        init_completion(&we.com);
 264
 265        /* Send the open event to OS/400 */
 266        hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
 267                        HvLpEvent_Type_VirtualIo,
 268                        viomajorsubtype_blockio | vioblockopen,
 269                        HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
 270                        viopath_sourceinst(viopath_hostLp),
 271                        viopath_targetinst(viopath_hostLp),
 272                        (u64)(unsigned long)&we, VIOVERSION << 16,
 273                        ((u64)unit << 48) | ((u64)flags<< 32),
 274                        0, 0, 0);
 275        if (hvrc != 0) {
 276                printk(KERN_WARNING "probe_disk: bad rc on HV open %d\n",
 277                        (int)hvrc);
 278                return;
 279        }
 280
 281        wait_for_completion(&we.com);
 282
 283        if (we.rc != 0) {
 284                if (flags != 0)
 285                        return;
 286                /* try again with read only flag set */
 287                flags = vioblockflags_ro;
 288                goto retry;
 289        }
 290
 291        /* Send the close event to OS/400.  We DON'T expect a response */
 292        hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
 293                        HvLpEvent_Type_VirtualIo,
 294                        viomajorsubtype_blockio | vioblockclose,
 295                        HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck,
 296                        viopath_sourceinst(viopath_hostLp),
 297                        viopath_targetinst(viopath_hostLp),
 298                        0, VIOVERSION << 16,
 299                        ((u64)unit << 48) | ((u64)flags << 32),
 300                        0, 0, 0);
 301        if (hvrc != 0) {
 302                printk(KERN_WARNING "probe_disk: "
 303                       "bad rc sending event to OS/400 %d\n", (int)hvrc);
 304                return;
 305        }
 306
 307        do_device_node(vio_root, "viodasd", FIRST_VIODASD + unit, unit,
 308                        "block", "IBM,iSeries-viodasd", NULL);
 309}
 310
 311static void __init get_viodasd_info(struct device_node *vio_root)
 312{
 313        int rc;
 314        u32 unit;
 315
 316        rc = viopath_open(viopath_hostLp, viomajorsubtype_blockio, 2);
 317        if (rc) {
 318                printk(KERN_WARNING "get_viodasd_info: "
 319                       "error opening path to host partition %d\n",
 320                       viopath_hostLp);
 321                return;
 322        }
 323
 324        /* Initialize our request handler */
 325        vio_setHandler(viomajorsubtype_blockio, handle_block_event);
 326
 327        for (unit = 0; unit < HVMAXARCHITECTEDVIRTUALDISKS; unit++)
 328                probe_disk(vio_root, unit);
 329
 330        vio_clearHandler(viomajorsubtype_blockio);
 331        viopath_close(viopath_hostLp, viomajorsubtype_blockio, 2);
 332}
 333
 334static void __init handle_cd_event(struct HvLpEvent *event)
 335{
 336        struct viocdlpevent *bevent;
 337        struct vio_waitevent *pwe;
 338
 339        if (!event)
 340                /* Notification that a partition went away! */
 341                return;
 342
 343        /* First, we should NEVER get an int here...only acks */
 344        if (hvlpevent_is_int(event)) {
 345                printk(KERN_WARNING "handle_cd_event: got an unexpected int\n");
 346                if (hvlpevent_need_ack(event)) {
 347                        event->xRc = HvLpEvent_Rc_InvalidSubtype;
 348                        HvCallEvent_ackLpEvent(event);
 349                }
 350                return;
 351        }
 352
 353        bevent = (struct viocdlpevent *)event;
 354
 355        switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
 356        case viocdgetinfo:
 357                pwe = (struct vio_waitevent *)event->xCorrelationToken;
 358                pwe->rc = event->xRc;
 359                pwe->sub_result = bevent->sub_result;
 360                complete(&pwe->com);
 361                break;
 362
 363        default:
 364                printk(KERN_WARNING "handle_cd_event: "
 365                        "message with unexpected subtype %0x04X!\n",
 366                        event->xSubtype & VIOMINOR_SUBTYPE_MASK);
 367                if (hvlpevent_need_ack(event)) {
 368                        event->xRc = HvLpEvent_Rc_InvalidSubtype;
 369                        HvCallEvent_ackLpEvent(event);
 370                }
 371        }
 372}
 373
 374static void __init get_viocd_info(struct device_node *vio_root)
 375{
 376        HvLpEvent_Rc hvrc;
 377        u32 unit;
 378        struct vio_waitevent we;
 379        struct vio_resource *unitinfo;
 380        dma_addr_t unitinfo_dmaaddr;
 381        int ret;
 382
 383        ret = viopath_open(viopath_hostLp, viomajorsubtype_cdio, 2);
 384        if (ret) {
 385                printk(KERN_WARNING
 386                        "get_viocd_info: error opening path to host partition %d\n",
 387                        viopath_hostLp);
 388                return;
 389        }
 390
 391        /* Initialize our request handler */
 392        vio_setHandler(viomajorsubtype_cdio, handle_cd_event);
 393
 394        unitinfo = iseries_hv_alloc(
 395                        sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS,
 396                        &unitinfo_dmaaddr, GFP_ATOMIC);
 397        if (!unitinfo) {
 398                printk(KERN_WARNING
 399                        "get_viocd_info: error allocating unitinfo\n");
 400                goto clear_handler;
 401        }
 402
 403        memset(unitinfo, 0, sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS);
 404
 405        init_completion(&we.com);
 406
 407        hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
 408                        HvLpEvent_Type_VirtualIo,
 409                        viomajorsubtype_cdio | viocdgetinfo,
 410                        HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
 411                        viopath_sourceinst(viopath_hostLp),
 412                        viopath_targetinst(viopath_hostLp),
 413                        (u64)&we, VIOVERSION << 16, unitinfo_dmaaddr, 0,
 414                        sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS, 0);
 415        if (hvrc != HvLpEvent_Rc_Good) {
 416                printk(KERN_WARNING
 417                        "get_viocd_info: cdrom error sending event. rc %d\n",
 418                        (int)hvrc);
 419                goto hv_free;
 420        }
 421
 422        wait_for_completion(&we.com);
 423
 424        if (we.rc) {
 425                printk(KERN_WARNING "get_viocd_info: bad rc %d:0x%04X\n",
 426                        we.rc, we.sub_result);
 427                goto hv_free;
 428        }
 429
 430        for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALCDROMS) &&
 431                        unitinfo[unit].rsrcname[0]; unit++) {
 432                if (!do_device_node(vio_root, "viocd", FIRST_VIOCD + unit, unit,
 433                                "block", "IBM,iSeries-viocd", &unitinfo[unit]))
 434                        break;
 435        }
 436
 437 hv_free:
 438        iseries_hv_free(sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS,
 439                        unitinfo, unitinfo_dmaaddr);
 440 clear_handler:
 441        vio_clearHandler(viomajorsubtype_cdio);
 442        viopath_close(viopath_hostLp, viomajorsubtype_cdio, 2);
 443}
 444
 445/* Handle interrupt events for tape */
 446static void __init handle_tape_event(struct HvLpEvent *event)
 447{
 448        struct vio_waitevent *we;
 449        struct viotapelpevent *tevent = (struct viotapelpevent *)event;
 450
 451        if (event == NULL)
 452                /* Notification that a partition went away! */
 453                return;
 454
 455        we = (struct vio_waitevent *)event->xCorrelationToken;
 456        switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
 457        case viotapegetinfo:
 458                we->rc = tevent->sub_type_result;
 459                complete(&we->com);
 460                break;
 461        default:
 462                printk(KERN_WARNING "handle_tape_event: weird ack\n");
 463        }
 464}
 465
 466static void __init get_viotape_info(struct device_node *vio_root)
 467{
 468        HvLpEvent_Rc hvrc;
 469        u32 unit;
 470        struct vio_resource *unitinfo;
 471        dma_addr_t unitinfo_dmaaddr;
 472        size_t len = sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALTAPES;
 473        struct vio_waitevent we;
 474        int ret;
 475
 476        init_completion(&we.com);
 477
 478        ret = viopath_open(viopath_hostLp, viomajorsubtype_tape, 2);
 479        if (ret) {
 480                printk(KERN_WARNING "get_viotape_info: "
 481                        "error on viopath_open to hostlp %d\n", ret);
 482                return;
 483        }
 484
 485        vio_setHandler(viomajorsubtype_tape, handle_tape_event);
 486
 487        unitinfo = iseries_hv_alloc(len, &unitinfo_dmaaddr, GFP_ATOMIC);
 488        if (!unitinfo)
 489                goto clear_handler;
 490
 491        memset(unitinfo, 0, len);
 492
 493        hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
 494                        HvLpEvent_Type_VirtualIo,
 495                        viomajorsubtype_tape | viotapegetinfo,
 496                        HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
 497                        viopath_sourceinst(viopath_hostLp),
 498                        viopath_targetinst(viopath_hostLp),
 499                        (u64)(unsigned long)&we, VIOVERSION << 16,
 500                        unitinfo_dmaaddr, len, 0, 0);
 501        if (hvrc != HvLpEvent_Rc_Good) {
 502                printk(KERN_WARNING "get_viotape_info: hv error on op %d\n",
 503                                (int)hvrc);
 504                goto hv_free;
 505        }
 506
 507        wait_for_completion(&we.com);
 508
 509        for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALTAPES) &&
 510                        unitinfo[unit].rsrcname[0]; unit++) {
 511                if (!do_device_node(vio_root, "viotape", FIRST_VIOTAPE + unit,
 512                                unit, "byte", "IBM,iSeries-viotape",
 513                                &unitinfo[unit]))
 514                        break;
 515        }
 516
 517 hv_free:
 518        iseries_hv_free(len, unitinfo, unitinfo_dmaaddr);
 519 clear_handler:
 520        vio_clearHandler(viomajorsubtype_tape);
 521        viopath_close(viopath_hostLp, viomajorsubtype_tape, 2);
 522}
 523
 524static int __init iseries_vio_init(void)
 525{
 526        struct device_node *vio_root;
 527        int ret = -ENODEV;
 528
 529        if (!firmware_has_feature(FW_FEATURE_ISERIES))
 530                goto out;
 531
 532        iommu_vio_init();
 533
 534        vio_root = of_find_node_by_path("/vdevice");
 535        if (!vio_root)
 536                goto out;
 537
 538        if (viopath_hostLp == HvLpIndexInvalid) {
 539                vio_set_hostlp();
 540                /* If we don't have a host, bail out */
 541                if (viopath_hostLp == HvLpIndexInvalid)
 542                        goto put_node;
 543        }
 544
 545        get_viodasd_info(vio_root);
 546        get_viocd_info(vio_root);
 547        get_viotape_info(vio_root);
 548
 549        ret = 0;
 550
 551 put_node:
 552        of_node_put(vio_root);
 553 out:
 554        return ret;
 555}
 556arch_initcall(iseries_vio_init);
 557