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), ®) || 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