| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * IBM PowerPC Virtual I/O Infrastructure Support. |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 7 | 8 | * Hollis Blanchard <hollisb@us.ibm.com> |
|---|
| 8 | 9 | * Stephen Rothwell |
|---|
| 9 | 10 | * Robert Jennings <rcjenn@us.ibm.com> |
|---|
| 10 | | - * |
|---|
| 11 | | - * This program is free software; you can redistribute it and/or |
|---|
| 12 | | - * modify it under the terms of the GNU General Public License |
|---|
| 13 | | - * as published by the Free Software Foundation; either version |
|---|
| 14 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 15 | 11 | */ |
|---|
| 16 | 12 | |
|---|
| 17 | 13 | #include <linux/cpu.h> |
|---|
| .. | .. |
|---|
| 24 | 20 | #include <linux/console.h> |
|---|
| 25 | 21 | #include <linux/export.h> |
|---|
| 26 | 22 | #include <linux/mm.h> |
|---|
| 27 | | -#include <linux/dma-mapping.h> |
|---|
| 23 | +#include <linux/dma-map-ops.h> |
|---|
| 28 | 24 | #include <linux/kobject.h> |
|---|
| 29 | 25 | |
|---|
| 30 | 26 | #include <asm/iommu.h> |
|---|
| .. | .. |
|---|
| 35 | 31 | #include <asm/tce.h> |
|---|
| 36 | 32 | #include <asm/page.h> |
|---|
| 37 | 33 | #include <asm/hvcall.h> |
|---|
| 34 | +#include <asm/machdep.h> |
|---|
| 38 | 35 | |
|---|
| 39 | 36 | static struct vio_dev vio_bus_device = { /* fake "parent" device */ |
|---|
| 40 | 37 | .name = "vio", |
|---|
| .. | .. |
|---|
| 492 | 489 | return NULL; |
|---|
| 493 | 490 | } |
|---|
| 494 | 491 | |
|---|
| 495 | | - ret = dma_iommu_ops.alloc(dev, size, dma_handle, flag, attrs); |
|---|
| 492 | + ret = iommu_alloc_coherent(dev, get_iommu_table_base(dev), size, |
|---|
| 493 | + dma_handle, dev->coherent_dma_mask, flag, |
|---|
| 494 | + dev_to_node(dev)); |
|---|
| 496 | 495 | if (unlikely(ret == NULL)) { |
|---|
| 497 | 496 | vio_cmo_dealloc(viodev, roundup(size, PAGE_SIZE)); |
|---|
| 498 | 497 | atomic_inc(&viodev->cmo.allocs_failed); |
|---|
| .. | .. |
|---|
| 507 | 506 | { |
|---|
| 508 | 507 | struct vio_dev *viodev = to_vio_dev(dev); |
|---|
| 509 | 508 | |
|---|
| 510 | | - dma_iommu_ops.free(dev, size, vaddr, dma_handle, attrs); |
|---|
| 511 | | - |
|---|
| 509 | + iommu_free_coherent(get_iommu_table_base(dev), size, vaddr, dma_handle); |
|---|
| 512 | 510 | vio_cmo_dealloc(viodev, roundup(size, PAGE_SIZE)); |
|---|
| 513 | 511 | } |
|---|
| 514 | 512 | |
|---|
| .. | .. |
|---|
| 518 | 516 | unsigned long attrs) |
|---|
| 519 | 517 | { |
|---|
| 520 | 518 | struct vio_dev *viodev = to_vio_dev(dev); |
|---|
| 521 | | - struct iommu_table *tbl; |
|---|
| 522 | | - dma_addr_t ret = IOMMU_MAPPING_ERROR; |
|---|
| 519 | + struct iommu_table *tbl = get_iommu_table_base(dev); |
|---|
| 520 | + dma_addr_t ret = DMA_MAPPING_ERROR; |
|---|
| 523 | 521 | |
|---|
| 524 | | - tbl = get_iommu_table_base(dev); |
|---|
| 525 | | - if (vio_cmo_alloc(viodev, roundup(size, IOMMU_PAGE_SIZE(tbl)))) { |
|---|
| 526 | | - atomic_inc(&viodev->cmo.allocs_failed); |
|---|
| 527 | | - return ret; |
|---|
| 528 | | - } |
|---|
| 529 | | - |
|---|
| 530 | | - ret = dma_iommu_ops.map_page(dev, page, offset, size, direction, attrs); |
|---|
| 531 | | - if (unlikely(dma_mapping_error(dev, ret))) { |
|---|
| 532 | | - vio_cmo_dealloc(viodev, roundup(size, IOMMU_PAGE_SIZE(tbl))); |
|---|
| 533 | | - atomic_inc(&viodev->cmo.allocs_failed); |
|---|
| 534 | | - } |
|---|
| 535 | | - |
|---|
| 522 | + if (vio_cmo_alloc(viodev, roundup(size, IOMMU_PAGE_SIZE(tbl)))) |
|---|
| 523 | + goto out_fail; |
|---|
| 524 | + ret = iommu_map_page(dev, tbl, page, offset, size, dma_get_mask(dev), |
|---|
| 525 | + direction, attrs); |
|---|
| 526 | + if (unlikely(ret == DMA_MAPPING_ERROR)) |
|---|
| 527 | + goto out_deallocate; |
|---|
| 536 | 528 | return ret; |
|---|
| 529 | + |
|---|
| 530 | +out_deallocate: |
|---|
| 531 | + vio_cmo_dealloc(viodev, roundup(size, IOMMU_PAGE_SIZE(tbl))); |
|---|
| 532 | +out_fail: |
|---|
| 533 | + atomic_inc(&viodev->cmo.allocs_failed); |
|---|
| 534 | + return DMA_MAPPING_ERROR; |
|---|
| 537 | 535 | } |
|---|
| 538 | 536 | |
|---|
| 539 | 537 | static void vio_dma_iommu_unmap_page(struct device *dev, dma_addr_t dma_handle, |
|---|
| .. | .. |
|---|
| 542 | 540 | unsigned long attrs) |
|---|
| 543 | 541 | { |
|---|
| 544 | 542 | struct vio_dev *viodev = to_vio_dev(dev); |
|---|
| 545 | | - struct iommu_table *tbl; |
|---|
| 543 | + struct iommu_table *tbl = get_iommu_table_base(dev); |
|---|
| 546 | 544 | |
|---|
| 547 | | - tbl = get_iommu_table_base(dev); |
|---|
| 548 | | - dma_iommu_ops.unmap_page(dev, dma_handle, size, direction, attrs); |
|---|
| 549 | | - |
|---|
| 545 | + iommu_unmap_page(tbl, dma_handle, size, direction, attrs); |
|---|
| 550 | 546 | vio_cmo_dealloc(viodev, roundup(size, IOMMU_PAGE_SIZE(tbl))); |
|---|
| 551 | 547 | } |
|---|
| 552 | 548 | |
|---|
| .. | .. |
|---|
| 555 | 551 | unsigned long attrs) |
|---|
| 556 | 552 | { |
|---|
| 557 | 553 | struct vio_dev *viodev = to_vio_dev(dev); |
|---|
| 558 | | - struct iommu_table *tbl; |
|---|
| 554 | + struct iommu_table *tbl = get_iommu_table_base(dev); |
|---|
| 559 | 555 | struct scatterlist *sgl; |
|---|
| 560 | 556 | int ret, count; |
|---|
| 561 | 557 | size_t alloc_size = 0; |
|---|
| 562 | 558 | |
|---|
| 563 | | - tbl = get_iommu_table_base(dev); |
|---|
| 564 | 559 | for_each_sg(sglist, sgl, nelems, count) |
|---|
| 565 | 560 | alloc_size += roundup(sgl->length, IOMMU_PAGE_SIZE(tbl)); |
|---|
| 566 | 561 | |
|---|
| 567 | | - if (vio_cmo_alloc(viodev, alloc_size)) { |
|---|
| 568 | | - atomic_inc(&viodev->cmo.allocs_failed); |
|---|
| 569 | | - return 0; |
|---|
| 570 | | - } |
|---|
| 571 | | - |
|---|
| 572 | | - ret = dma_iommu_ops.map_sg(dev, sglist, nelems, direction, attrs); |
|---|
| 573 | | - |
|---|
| 574 | | - if (unlikely(!ret)) { |
|---|
| 575 | | - vio_cmo_dealloc(viodev, alloc_size); |
|---|
| 576 | | - atomic_inc(&viodev->cmo.allocs_failed); |
|---|
| 577 | | - return ret; |
|---|
| 578 | | - } |
|---|
| 562 | + if (vio_cmo_alloc(viodev, alloc_size)) |
|---|
| 563 | + goto out_fail; |
|---|
| 564 | + ret = ppc_iommu_map_sg(dev, tbl, sglist, nelems, dma_get_mask(dev), |
|---|
| 565 | + direction, attrs); |
|---|
| 566 | + if (unlikely(!ret)) |
|---|
| 567 | + goto out_deallocate; |
|---|
| 579 | 568 | |
|---|
| 580 | 569 | for_each_sg(sglist, sgl, ret, count) |
|---|
| 581 | 570 | alloc_size -= roundup(sgl->dma_length, IOMMU_PAGE_SIZE(tbl)); |
|---|
| 582 | 571 | if (alloc_size) |
|---|
| 583 | 572 | vio_cmo_dealloc(viodev, alloc_size); |
|---|
| 584 | | - |
|---|
| 585 | 573 | return ret; |
|---|
| 574 | + |
|---|
| 575 | +out_deallocate: |
|---|
| 576 | + vio_cmo_dealloc(viodev, alloc_size); |
|---|
| 577 | +out_fail: |
|---|
| 578 | + atomic_inc(&viodev->cmo.allocs_failed); |
|---|
| 579 | + return 0; |
|---|
| 586 | 580 | } |
|---|
| 587 | 581 | |
|---|
| 588 | 582 | static void vio_dma_iommu_unmap_sg(struct device *dev, |
|---|
| .. | .. |
|---|
| 591 | 585 | unsigned long attrs) |
|---|
| 592 | 586 | { |
|---|
| 593 | 587 | struct vio_dev *viodev = to_vio_dev(dev); |
|---|
| 594 | | - struct iommu_table *tbl; |
|---|
| 588 | + struct iommu_table *tbl = get_iommu_table_base(dev); |
|---|
| 595 | 589 | struct scatterlist *sgl; |
|---|
| 596 | 590 | size_t alloc_size = 0; |
|---|
| 597 | 591 | int count; |
|---|
| 598 | 592 | |
|---|
| 599 | | - tbl = get_iommu_table_base(dev); |
|---|
| 600 | 593 | for_each_sg(sglist, sgl, nelems, count) |
|---|
| 601 | 594 | alloc_size += roundup(sgl->dma_length, IOMMU_PAGE_SIZE(tbl)); |
|---|
| 602 | 595 | |
|---|
| 603 | | - dma_iommu_ops.unmap_sg(dev, sglist, nelems, direction, attrs); |
|---|
| 604 | | - |
|---|
| 596 | + ppc_iommu_unmap_sg(tbl, sglist, nelems, direction, attrs); |
|---|
| 605 | 597 | vio_cmo_dealloc(viodev, alloc_size); |
|---|
| 606 | | -} |
|---|
| 607 | | - |
|---|
| 608 | | -static int vio_dma_iommu_dma_supported(struct device *dev, u64 mask) |
|---|
| 609 | | -{ |
|---|
| 610 | | - return dma_iommu_ops.dma_supported(dev, mask); |
|---|
| 611 | | -} |
|---|
| 612 | | - |
|---|
| 613 | | -static u64 vio_dma_get_required_mask(struct device *dev) |
|---|
| 614 | | -{ |
|---|
| 615 | | - return dma_iommu_ops.get_required_mask(dev); |
|---|
| 616 | 598 | } |
|---|
| 617 | 599 | |
|---|
| 618 | 600 | static const struct dma_map_ops vio_dma_mapping_ops = { |
|---|
| 619 | 601 | .alloc = vio_dma_iommu_alloc_coherent, |
|---|
| 620 | 602 | .free = vio_dma_iommu_free_coherent, |
|---|
| 621 | | - .mmap = dma_nommu_mmap_coherent, |
|---|
| 622 | 603 | .map_sg = vio_dma_iommu_map_sg, |
|---|
| 623 | 604 | .unmap_sg = vio_dma_iommu_unmap_sg, |
|---|
| 624 | 605 | .map_page = vio_dma_iommu_map_page, |
|---|
| 625 | 606 | .unmap_page = vio_dma_iommu_unmap_page, |
|---|
| 626 | | - .dma_supported = vio_dma_iommu_dma_supported, |
|---|
| 627 | | - .get_required_mask = vio_dma_get_required_mask, |
|---|
| 628 | | - .mapping_error = dma_iommu_mapping_error, |
|---|
| 607 | + .dma_supported = dma_iommu_dma_supported, |
|---|
| 608 | + .get_required_mask = dma_iommu_get_required_mask, |
|---|
| 609 | + .mmap = dma_common_mmap, |
|---|
| 610 | + .get_sgtable = dma_common_get_sgtable, |
|---|
| 611 | + .alloc_pages = dma_common_alloc_pages, |
|---|
| 612 | + .free_pages = dma_common_free_pages, |
|---|
| 629 | 613 | }; |
|---|
| 630 | 614 | |
|---|
| 631 | 615 | /** |
|---|
| .. | .. |
|---|
| 1214 | 1198 | else |
|---|
| 1215 | 1199 | tbl->it_ops = &iommu_table_pseries_ops; |
|---|
| 1216 | 1200 | |
|---|
| 1217 | | - return iommu_init_table(tbl, -1); |
|---|
| 1201 | + return iommu_init_table(tbl, -1, 0, 0); |
|---|
| 1218 | 1202 | } |
|---|
| 1219 | 1203 | |
|---|
| 1220 | 1204 | /** |
|---|
| .. | .. |
|---|
| 1302 | 1286 | int __vio_register_driver(struct vio_driver *viodrv, struct module *owner, |
|---|
| 1303 | 1287 | const char *mod_name) |
|---|
| 1304 | 1288 | { |
|---|
| 1289 | + // vio_bus_type is only initialised for pseries |
|---|
| 1290 | + if (!machine_is(pseries)) |
|---|
| 1291 | + return -ENODEV; |
|---|
| 1292 | + |
|---|
| 1305 | 1293 | pr_debug("%s: driver %s registering\n", __func__, viodrv->name); |
|---|
| 1306 | 1294 | |
|---|
| 1307 | 1295 | /* fill in 'struct driver' fields */ |
|---|
| .. | .. |
|---|
| 1351 | 1339 | struct device_node *parent_node; |
|---|
| 1352 | 1340 | const __be32 *prop; |
|---|
| 1353 | 1341 | enum vio_dev_family family; |
|---|
| 1354 | | - const char *of_node_name = of_node->name ? of_node->name : "<unknown>"; |
|---|
| 1355 | 1342 | |
|---|
| 1356 | 1343 | /* |
|---|
| 1357 | 1344 | * Determine if this node is a under the /vdevice node or under the |
|---|
| .. | .. |
|---|
| 1359 | 1346 | */ |
|---|
| 1360 | 1347 | parent_node = of_get_parent(of_node); |
|---|
| 1361 | 1348 | if (parent_node) { |
|---|
| 1362 | | - if (!strcmp(parent_node->type, "ibm,platform-facilities")) |
|---|
| 1349 | + if (of_node_is_type(parent_node, "ibm,platform-facilities")) |
|---|
| 1363 | 1350 | family = PFO; |
|---|
| 1364 | | - else if (!strcmp(parent_node->type, "vdevice")) |
|---|
| 1351 | + else if (of_node_is_type(parent_node, "vdevice")) |
|---|
| 1365 | 1352 | family = VDEVICE; |
|---|
| 1366 | 1353 | else { |
|---|
| 1367 | | - pr_warn("%s: parent(%pOF) of %s not recognized.\n", |
|---|
| 1354 | + pr_warn("%s: parent(%pOF) of %pOFn not recognized.\n", |
|---|
| 1368 | 1355 | __func__, |
|---|
| 1369 | 1356 | parent_node, |
|---|
| 1370 | | - of_node_name); |
|---|
| 1357 | + of_node); |
|---|
| 1371 | 1358 | of_node_put(parent_node); |
|---|
| 1372 | 1359 | return NULL; |
|---|
| 1373 | 1360 | } |
|---|
| 1374 | 1361 | of_node_put(parent_node); |
|---|
| 1375 | 1362 | } else { |
|---|
| 1376 | | - pr_warn("%s: could not determine the parent of node %s.\n", |
|---|
| 1377 | | - __func__, of_node_name); |
|---|
| 1363 | + pr_warn("%s: could not determine the parent of node %pOFn.\n", |
|---|
| 1364 | + __func__, of_node); |
|---|
| 1378 | 1365 | return NULL; |
|---|
| 1379 | 1366 | } |
|---|
| 1380 | 1367 | |
|---|
| 1381 | 1368 | if (family == PFO) { |
|---|
| 1382 | 1369 | if (of_get_property(of_node, "interrupt-controller", NULL)) { |
|---|
| 1383 | | - pr_debug("%s: Skipping the interrupt controller %s.\n", |
|---|
| 1384 | | - __func__, of_node_name); |
|---|
| 1370 | + pr_debug("%s: Skipping the interrupt controller %pOFn.\n", |
|---|
| 1371 | + __func__, of_node); |
|---|
| 1385 | 1372 | return NULL; |
|---|
| 1386 | 1373 | } |
|---|
| 1387 | 1374 | } |
|---|
| .. | .. |
|---|
| 1398 | 1385 | if (viodev->family == VDEVICE) { |
|---|
| 1399 | 1386 | unsigned int unit_address; |
|---|
| 1400 | 1387 | |
|---|
| 1401 | | - if (of_node->type != NULL) |
|---|
| 1402 | | - viodev->type = of_node->type; |
|---|
| 1403 | | - else { |
|---|
| 1404 | | - pr_warn("%s: node %s is missing the 'device_type' " |
|---|
| 1405 | | - "property.\n", __func__, of_node_name); |
|---|
| 1388 | + viodev->type = of_node_get_device_type(of_node); |
|---|
| 1389 | + if (!viodev->type) { |
|---|
| 1390 | + pr_warn("%s: node %pOFn is missing the 'device_type' " |
|---|
| 1391 | + "property.\n", __func__, of_node); |
|---|
| 1406 | 1392 | goto out; |
|---|
| 1407 | 1393 | } |
|---|
| 1408 | 1394 | |
|---|
| 1409 | 1395 | prop = of_get_property(of_node, "reg", NULL); |
|---|
| 1410 | 1396 | if (prop == NULL) { |
|---|
| 1411 | | - pr_warn("%s: node %s missing 'reg'\n", |
|---|
| 1412 | | - __func__, of_node_name); |
|---|
| 1397 | + pr_warn("%s: node %pOFn missing 'reg'\n", |
|---|
| 1398 | + __func__, of_node); |
|---|
| 1413 | 1399 | goto out; |
|---|
| 1414 | 1400 | } |
|---|
| 1415 | 1401 | unit_address = of_read_number(prop, 1); |
|---|
| .. | .. |
|---|
| 1424 | 1410 | if (prop != NULL) |
|---|
| 1425 | 1411 | viodev->resource_id = of_read_number(prop, 1); |
|---|
| 1426 | 1412 | |
|---|
| 1427 | | - dev_set_name(&viodev->dev, "%s", of_node_name); |
|---|
| 1428 | | - viodev->type = of_node_name; |
|---|
| 1413 | + dev_set_name(&viodev->dev, "%pOFn", of_node); |
|---|
| 1414 | + viodev->type = dev_name(&viodev->dev); |
|---|
| 1429 | 1415 | viodev->irq = 0; |
|---|
| 1430 | 1416 | } |
|---|
| 1431 | 1417 | |
|---|
| .. | .. |
|---|
| 1534 | 1520 | |
|---|
| 1535 | 1521 | return 0; |
|---|
| 1536 | 1522 | } |
|---|
| 1537 | | -postcore_initcall(vio_bus_init); |
|---|
| 1523 | +machine_postcore_initcall(pseries, vio_bus_init); |
|---|
| 1538 | 1524 | |
|---|
| 1539 | 1525 | static int __init vio_device_init(void) |
|---|
| 1540 | 1526 | { |
|---|
| .. | .. |
|---|
| 1543 | 1529 | |
|---|
| 1544 | 1530 | return 0; |
|---|
| 1545 | 1531 | } |
|---|
| 1546 | | -device_initcall(vio_device_init); |
|---|
| 1532 | +machine_device_initcall(pseries, vio_device_init); |
|---|
| 1547 | 1533 | |
|---|
| 1548 | 1534 | static ssize_t name_show(struct device *dev, |
|---|
| 1549 | 1535 | struct device_attribute *attr, char *buf) |
|---|
| .. | .. |
|---|
| 1649 | 1635 | } |
|---|
| 1650 | 1636 | EXPORT_SYMBOL(vio_get_attribute); |
|---|
| 1651 | 1637 | |
|---|
| 1652 | | -#ifdef CONFIG_PPC_PSERIES |
|---|
| 1653 | 1638 | /* vio_find_name() - internal because only vio.c knows how we formatted the |
|---|
| 1654 | 1639 | * kobject name |
|---|
| 1655 | 1640 | */ |
|---|
| .. | .. |
|---|
| 1675 | 1660 | { |
|---|
| 1676 | 1661 | char kobj_name[20]; |
|---|
| 1677 | 1662 | struct device_node *vnode_parent; |
|---|
| 1678 | | - const char *dev_type; |
|---|
| 1679 | 1663 | |
|---|
| 1680 | 1664 | vnode_parent = of_get_parent(vnode); |
|---|
| 1681 | 1665 | if (!vnode_parent) |
|---|
| 1682 | 1666 | return NULL; |
|---|
| 1683 | 1667 | |
|---|
| 1684 | | - dev_type = of_get_property(vnode_parent, "device_type", NULL); |
|---|
| 1685 | | - of_node_put(vnode_parent); |
|---|
| 1686 | | - if (!dev_type) |
|---|
| 1687 | | - return NULL; |
|---|
| 1688 | | - |
|---|
| 1689 | 1668 | /* construct the kobject name from the device node */ |
|---|
| 1690 | | - if (!strcmp(dev_type, "vdevice")) { |
|---|
| 1669 | + if (of_node_is_type(vnode_parent, "vdevice")) { |
|---|
| 1691 | 1670 | const __be32 *prop; |
|---|
| 1692 | 1671 | |
|---|
| 1693 | 1672 | prop = of_get_property(vnode, "reg", NULL); |
|---|
| 1694 | 1673 | if (!prop) |
|---|
| 1695 | | - return NULL; |
|---|
| 1674 | + goto out; |
|---|
| 1696 | 1675 | snprintf(kobj_name, sizeof(kobj_name), "%x", |
|---|
| 1697 | 1676 | (uint32_t)of_read_number(prop, 1)); |
|---|
| 1698 | | - } else if (!strcmp(dev_type, "ibm,platform-facilities")) |
|---|
| 1699 | | - snprintf(kobj_name, sizeof(kobj_name), "%s", vnode->name); |
|---|
| 1677 | + } else if (of_node_is_type(vnode_parent, "ibm,platform-facilities")) |
|---|
| 1678 | + snprintf(kobj_name, sizeof(kobj_name), "%pOFn", vnode); |
|---|
| 1700 | 1679 | else |
|---|
| 1701 | | - return NULL; |
|---|
| 1680 | + goto out; |
|---|
| 1702 | 1681 | |
|---|
| 1682 | + of_node_put(vnode_parent); |
|---|
| 1703 | 1683 | return vio_find_name(kobj_name); |
|---|
| 1684 | +out: |
|---|
| 1685 | + of_node_put(vnode_parent); |
|---|
| 1686 | + return NULL; |
|---|
| 1704 | 1687 | } |
|---|
| 1705 | 1688 | EXPORT_SYMBOL(vio_find_node); |
|---|
| 1706 | 1689 | |
|---|
| .. | .. |
|---|
| 1721 | 1704 | return rc; |
|---|
| 1722 | 1705 | } |
|---|
| 1723 | 1706 | EXPORT_SYMBOL(vio_disable_interrupts); |
|---|
| 1724 | | -#endif /* CONFIG_PPC_PSERIES */ |
|---|
| 1707 | + |
|---|
| 1708 | +static int __init vio_init(void) |
|---|
| 1709 | +{ |
|---|
| 1710 | + dma_debug_add_bus(&vio_bus_type); |
|---|
| 1711 | + return 0; |
|---|
| 1712 | +} |
|---|
| 1713 | +machine_fs_initcall(pseries, vio_init); |
|---|