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/gfp.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 = kmalloc(strlen(path) + 1, GFP_KERNEL);
  91        if (!np->full_name) {
  92                kfree(np);
  93                return NULL;
  94        }
  95        strcpy(np->full_name, path);
  96        of_node_set_flag(np, OF_DYNAMIC);
  97        kref_init(&np->kref);
  98        np->parent = of_node_get(parent);
  99        return np;
 100}
 101
 102static void free_node(struct device_node *np)
 103{
 104        struct property *next;
 105        struct property *prop;
 106
 107        next = np->properties;
 108        while (next) {
 109                prop = next;
 110                next = prop->next;
 111                free_property(prop);
 112        }
 113        of_node_put(np->parent);
 114        kfree(np->full_name);
 115        kfree(np);
 116}
 117
 118static int add_string_property(struct device_node *np, const char *name,
 119                const char *value)
 120{
 121        struct property *nprop = new_property(name, strlen(value) + 1, value);
 122
 123        if (!nprop)
 124                return 0;
 125        prom_add_property(np, nprop);
 126        return 1;
 127}
 128
 129static int add_raw_property(struct device_node *np, const char *name,
 130                int length, const void *value)
 131{
 132        struct property *nprop = new_property(name, length, value);
 133
 134        if (!nprop)
 135                return 0;
 136        prom_add_property(np, nprop);
 137        return 1;
 138}
 139
 140static struct device_node *do_device_node(struct device_node *parent,
 141                const char *name, u32 reg, u32 unit, const char *type,
 142                const char *compat, struct vio_resource *res)
 143{
 144        struct device_node *np;
 145        char path[32];
 146
 147        snprintf(path, sizeof(path), "/vdevice/%s@%08x", name, reg);
 148        np = new_node(path, parent);
 149        if (!np)
 150                return NULL;
 151        if (!add_string_property(np, "name", name) ||
 152                !add_string_property(np, "device_type", type) ||
 153                !add_string_property(np, "compatible", compat) ||
 154                !add_raw_property(np, "reg", sizeof(reg), &reg) ||
 155                !add_raw_property(np, "linux,unit_address",
 156                        sizeof(unit), &unit)) {
 157                goto node_free;
 158        }
 159        if (res) {
 160                if (!add_raw_property(np, "linux,vio_rsrcname",
 161                                sizeof(res->rsrcname), res->rsrcname) ||
 162                        !add_raw_property(np, "linux,vio_type",
 163                                sizeof(res->type), res->type) ||
 164                        !add_raw_property(np, "linux,vio_model",
 165                                sizeof(res->model), res->model))
 166                        goto node_free;
 167        }
 168        np->name = of_get_property(np, "name", NULL);
 169        np->type = of_get_property(np, "device_type", NULL);
 170        of_attach_node(np);
 171#ifdef CONFIG_PROC_DEVICETREE
 172        if (parent->pde) {
 173                struct proc_dir_entry *ent;
 174
 175                ent = proc_mkdir(strrchr(np->full_name, '/') + 1, parent->pde);
 176                if (ent)
 177                        proc_device_tree_add_node(np, ent);
 178        }
 179#endif
 180        return np;
 181
 182 node_free:
 183        free_node(np);
 184        return NULL;
 185}
 186
 187/*
 188 * This is here so that we can dynamically add viodasd
 189 * devices without exposing all the above infrastructure.
 190 */
 191struct vio_dev *vio_create_viodasd(u32 unit)
 192{
 193        struct device_node *vio_root;
 194        struct device_node *np;
 195        struct vio_dev *vdev = NULL;
 196
 197        vio_root = of_find_node_by_path("/vdevice");
 198        if (!vio_root)
 199                return NULL;
 200        np = do_device_node(vio_root, "viodasd", FIRST_VIODASD + unit, unit,
 201                        "block", "IBM,iSeries-viodasd", NULL);
 202        of_node_put(vio_root);
 203        if (np) {
 204                vdev = vio_register_device_node(np);
 205                if (!vdev)
 206                        free_node(np);
 207        }
 208        return vdev;
 209}
 210EXPORT_SYMBOL_GPL(vio_create_viodasd);
 211
 212static void __init handle_block_event(struct HvLpEvent *event)
 213{
 214        struct vioblocklpevent *bevent = (struct vioblocklpevent *)event;
 215        struct vio_waitevent *pwe;
 216
 217        if (event == NULL)
 218                /* Notification that a partition went away! */
 219                return;
 220        /* First, we should NEVER get an int here...only acks */
 221        if (hvlpevent_is_int(event)) {
 222                printk(KERN_WARNING "handle_viod_request: "
 223                       "Yikes! got an int in viodasd event handler!\n");
 224                if (hvlpevent_need_ack(event)) {
 225                        event->xRc = HvLpEvent_Rc_InvalidSubtype;
 226                        HvCallEvent_ackLpEvent(event);
 227                }
 228                return;
 229        }
 230
 231        switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
 232        case vioblockopen:
 233                /*
 234                 * Handle a response to an open request.  We get all the
 235                 * disk information in the response, so update it.  The
 236                 * correlation token contains a pointer to a waitevent
 237                 * structure that has a completion in it.  update the
 238                 * return code in the waitevent structure and post the
 239                 * completion to wake up the guy who sent the request
 240                 */
 241                pwe = (struct vio_waitevent *)event->xCorrelationToken;
 242                pwe->rc = event->xRc;
 243                pwe->sub_result = bevent->sub_result;
 244                complete(&pwe->com);
 245                break;
 246        case vioblockclose:
 247                break;
 248        default:
 249                printk(KERN_WARNING "handle_viod_request: unexpected subtype!");
 250                if (hvlpevent_need_ack(event)) {
 251                        event->xRc = HvLpEvent_Rc_InvalidSubtype;
 252                        HvCallEvent_ackLpEvent(event);
 253                }
 254        }
 255}
 256
 257static void __init probe_disk(struct device_node *vio_root, u32 unit)
 258{
 259        HvLpEvent_Rc hvrc;
 260        struct vio_waitevent we;
 261        u16 flags = 0;
 262
 263retry:
 264        init_completion(&we.com);
 265
 266        /* Send the open event to OS/400 */
 267        hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
 268                        HvLpEvent_Type_VirtualIo,
 269                        viomajorsubtype_blockio | vioblockopen,
 270                        HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
 271                        viopath_sourceinst(viopath_hostLp),
 272                        viopath_targetinst(viopath_hostLp),
 273                        (u64)(unsigned long)&we, VIOVERSION << 16,
 274                        ((u64)unit << 48) | ((u64)flags<< 32),
 275                        0, 0, 0);
 276        if (hvrc != 0) {
 277                printk(KERN_WARNING "probe_disk: bad rc on HV open %d\n",
 278                        (int)hvrc);
 279                return;
 280        }
 281
 282        wait_for_completion(&we.com);
 283
 284        if (we.rc != 0) {
 285                if (flags != 0)
 286                        return;
 287                /* try again with read only flag set */
 288                flags = vioblockflags_ro;
 289                goto retry;
 290        }
 291
 292        /* Send the close event to OS/400.  We DON'T expect a response */
 293        hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
 294                        HvLpEvent_Type_VirtualIo,
 295                        viomajorsubtype_blockio | vioblockclose,
 296                        HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck,
 297                        viopath_sourceinst(viopath_hostLp),
 298                        viopath_targetinst(viopath_hostLp),
 299                        0, VIOVERSION << 16,
 300                        ((u64)unit << 48) | ((u64)flags << 32),
 301                        0, 0, 0);
 302        if (hvrc != 0) {
 303                printk(KERN_WARNING "probe_disk: "
 304                       "bad rc sending event to OS/400 %d\n", (int)hvrc);
 305                return;
 306        }
 307
 308        do_device_node(vio_root, "viodasd", FIRST_VIODASD + unit, unit,
 309                        "block", "IBM,iSeries-viodasd", NULL);
 310}
 311
 312static void __init get_viodasd_info(struct device_node *vio_root)
 313{
 314        int rc;
 315        u32 unit;
 316
 317        rc = viopath_open(viopath_hostLp, viomajorsubtype_blockio, 2);
 318        if (rc) {
 319                printk(KERN_WARNING "get_viodasd_info: "
 320                       "error opening path to host partition %d\n",
 321                       viopath_hostLp);
 322                return;
 323        }
 324
 325        /* Initialize our request handler */
 326        vio_setHandler(viomajorsubtype_blockio, handle_block_event);
 327
 328        for (unit = 0; unit < HVMAXARCHITECTEDVIRTUALDISKS; unit++)
 329                probe_disk(vio_root, unit);
 330
 331        vio_clearHandler(viomajorsubtype_blockio);
 332        viopath_close(viopath_hostLp, viomajorsubtype_blockio, 2);
 333}
 334
 335static void __init handle_cd_event(struct HvLpEvent *event)
 336{
 337        struct viocdlpevent *bevent;
 338        struct vio_waitevent *pwe;
 339
 340        if (!event)
 341                /* Notification that a partition went away! */
 342                return;
 343
 344        /* First, we should NEVER get an int here...only acks */
 345        if (hvlpevent_is_int(event)) {
 346                printk(KERN_WARNING "handle_cd_event: got an unexpected int\n");
 347                if (hvlpevent_need_ack(event)) {
 348                        event->xRc = HvLpEvent_Rc_InvalidSubtype;
 349                        HvCallEvent_ackLpEvent(event);
 350                }
 351                return;
 352        }
 353
 354        bevent = (struct viocdlpevent *)event;
 355
 356        switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
 357        case viocdgetinfo:
 358                pwe = (struct vio_waitevent *)event->xCorrelationToken;
 359                pwe->rc = event->xRc;
 360                pwe->sub_result = bevent->sub_result;
 361                complete(&pwe->com);
 362                break;
 363
 364        default:
 365                printk(KERN_WARNING "handle_cd_event: "
 366                        "message with unexpected subtype %0x04X!\n",
 367                        event->xSubtype & VIOMINOR_SUBTYPE_MASK);
 368                if (hvlpevent_need_ack(event)) {
 369                        event->xRc = HvLpEvent_Rc_InvalidSubtype;
 370                        HvCallEvent_ackLpEvent(event);
 371                }
 372        }
 373}
 374
 375static void __init get_viocd_info(struct device_node *vio_root)
 376{
 377        HvLpEvent_Rc hvrc;
 378        u32 unit;
 379        struct vio_waitevent we;
 380        struct vio_resource *unitinfo;
 381        dma_addr_t unitinfo_dmaaddr;
 382        int ret;
 383
 384        ret = viopath_open(viopath_hostLp, viomajorsubtype_cdio, 2);
 385        if (ret) {
 386                printk(KERN_WARNING
 387                        "get_viocd_info: error opening path to host partition %d\n",
 388                        viopath_hostLp);
 389                return;
 390        }
 391
 392        /* Initialize our request handler */
 393        vio_setHandler(viomajorsubtype_cdio, handle_cd_event);
 394
 395        unitinfo = iseries_hv_alloc(
 396                        sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS,
 397                        &unitinfo_dmaaddr, GFP_ATOMIC);
 398        if (!unitinfo) {
 399                printk(KERN_WARNING
 400                        "get_viocd_info: error allocating unitinfo\n");
 401                goto clear_handler;
 402        }
 403
 404        memset(unitinfo, 0, sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS);
 405
 406        init_completion(&we.com);
 407
 408        hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
 409                        HvLpEvent_Type_VirtualIo,
 410                        viomajorsubtype_cdio | viocdgetinfo,
 411                        HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
 412                        viopath_sourceinst(viopath_hostLp),
 413                        viopath_targetinst(viopath_hostLp),
 414                        (u64)&we, VIOVERSION << 16, unitinfo_dmaaddr, 0,
 415                        sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS, 0);
 416        if (hvrc != HvLpEvent_Rc_Good) {
 417                printk(KERN_WARNING
 418                        "get_viocd_info: cdrom error sending event. rc %d\n",
 419                        (int)hvrc);
 420                goto hv_free;
 421        }
 422
 423        wait_for_completion(&we.com);
 424
 425        if (we.rc) {
 426                printk(KERN_WARNING "get_viocd_info: bad rc %d:0x%04X\n",
 427                        we.rc, we.sub_result);
 428                goto hv_free;
 429        }
 430
 431        for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALCDROMS) &&
 432                        unitinfo[unit].rsrcname[0]; unit++) {
 433                if (!do_device_node(vio_root, "viocd", FIRST_VIOCD + unit, unit,
 434                                "block", "IBM,iSeries-viocd", &unitinfo[unit]))
 435                        break;
 436        }
 437
 438 hv_free:
 439        iseries_hv_free(sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS,
 440                        unitinfo, unitinfo_dmaaddr);
 441 clear_handler:
 442        vio_clearHandler(viomajorsubtype_cdio);
 443        viopath_close(viopath_hostLp, viomajorsubtype_cdio, 2);
 444}
 445
 446/* Handle interrupt events for tape */
 447static void __init handle_tape_event(struct HvLpEvent *event)
 448{
 449        struct vio_waitevent *we;
 450        struct viotapelpevent *tevent = (struct viotapelpevent *)event;
 451
 452        if (event == NULL)
 453                /* Notification that a partition went away! */
 454                return;
 455
 456        we = (struct vio_waitevent *)event->xCorrelationToken;
 457        switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
 458        case viotapegetinfo:
 459                we->rc = tevent->sub_type_result;
 460                complete(&we->com);
 461                break;
 462        default:
 463                printk(KERN_WARNING "handle_tape_event: weird ack\n");
 464        }
 465}
 466
 467static void __init get_viotape_info(struct device_node *vio_root)
 468{
 469        HvLpEvent_Rc hvrc;
 470        u32 unit;
 471        struct vio_resource *unitinfo;
 472        dma_addr_t unitinfo_dmaaddr;
 473        size_t len = sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALTAPES;
 474        struct vio_waitevent we;
 475        int ret;
 476
 477        ret = viopath_open(viopath_hostLp, viomajorsubtype_tape, 2);
 478        if (ret) {
 479                printk(KERN_WARNING "get_viotape_info: "
 480                        "error on viopath_open to hostlp %d\n", ret);
 481                return;
 482        }
 483
 484        vio_setHandler(viomajorsubtype_tape, handle_tape_event);
 485
 486        unitinfo = iseries_hv_alloc(len, &unitinfo_dmaaddr, GFP_ATOMIC);
 487        if (!unitinfo)
 488                goto clear_handler;
 489
 490        memset(unitinfo, 0, len);
 491
 492        hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
 493                        HvLpEvent_Type_VirtualIo,
 494                        viomajorsubtype_tape | viotapegetinfo,
 495                        HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
 496                        viopath_sourceinst(viopath_hostLp),
 497                        viopath_targetinst(viopath_hostLp),
 498                        (u64)(unsigned long)&we, VIOVERSION << 16,
 499                        unitinfo_dmaaddr, len, 0, 0);
 500        if (hvrc != HvLpEvent_Rc_Good) {
 501                printk(KERN_WARNING "get_viotape_info: hv error on op %d\n",
 502                                (int)hvrc);
 503                goto hv_free;
 504        }
 505
 506        wait_for_completion(&we.com);
 507
 508        for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALTAPES) &&
 509                        unitinfo[unit].rsrcname[0]; unit++) {
 510                if (!do_device_node(vio_root, "viotape", FIRST_VIOTAPE + unit,
 511                                unit, "byte", "IBM,iSeries-viotape",
 512                                &unitinfo[unit]))
 513                        break;
 514        }
 515
 516 hv_free:
 517        iseries_hv_free(len, unitinfo, unitinfo_dmaaddr);
 518 clear_handler:
 519        vio_clearHandler(viomajorsubtype_tape);
 520        viopath_close(viopath_hostLp, viomajorsubtype_tape, 2);
 521}
 522
 523static int __init iseries_vio_init(void)
 524{
 525        struct device_node *vio_root;
 526        int ret = -ENODEV;
 527
 528        if (!firmware_has_feature(FW_FEATURE_ISERIES))
 529                goto out;
 530
 531        iommu_vio_init();
 532
 533        vio_root = of_find_node_by_path("/vdevice");
 534        if (!vio_root)
 535                goto out;
 536
 537        if (viopath_hostLp == HvLpIndexInvalid) {
 538                vio_set_hostlp();
 539                /* If we don't have a host, bail out */
 540                if (viopath_hostLp == HvLpIndexInvalid)
 541                        goto put_node;
 542        }
 543
 544        get_viodasd_info(vio_root);
 545        get_viocd_info(vio_root);
 546        get_viotape_info(vio_root);
 547
 548        ret = 0;
 549
 550 put_node:
 551        of_node_put(vio_root);
 552 out:
 553        return ret;
 554}
 555arch_initcall(iseries_vio_init);
 556