| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Virtio memory mapped device driver |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 48 | 49 | * virtio_mmio.device=0x100@0x100b0000:48 \ |
|---|
| 49 | 50 | * virtio_mmio.device=1K@0x1001e000:74 |
|---|
| 50 | 51 | * |
|---|
| 51 | | - * |
|---|
| 52 | | - * |
|---|
| 53 | 52 | * Based on Virtio PCI driver by Anthony Liguori, copyright IBM Corp. 2007 |
|---|
| 54 | | - * |
|---|
| 55 | | - * This work is licensed under the terms of the GNU GPL, version 2 or later. |
|---|
| 56 | | - * See the COPYING file in the top-level directory. |
|---|
| 57 | 53 | */ |
|---|
| 58 | 54 | |
|---|
| 59 | 55 | #define pr_fmt(fmt) "virtio-mmio: " fmt |
|---|
| .. | .. |
|---|
| 66 | 62 | #include <linux/list.h> |
|---|
| 67 | 63 | #include <linux/module.h> |
|---|
| 68 | 64 | #include <linux/platform_device.h> |
|---|
| 65 | +#include <linux/pm.h> |
|---|
| 69 | 66 | #include <linux/slab.h> |
|---|
| 70 | 67 | #include <linux/spinlock.h> |
|---|
| 71 | 68 | #include <linux/virtio.h> |
|---|
| .. | .. |
|---|
| 467 | 464 | struct irq_affinity *desc) |
|---|
| 468 | 465 | { |
|---|
| 469 | 466 | struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); |
|---|
| 470 | | - unsigned int irq = platform_get_irq(vm_dev->pdev, 0); |
|---|
| 471 | | - int i, err; |
|---|
| 467 | + int irq = platform_get_irq(vm_dev->pdev, 0); |
|---|
| 468 | + int i, err, queue_idx = 0; |
|---|
| 469 | + |
|---|
| 470 | + if (irq < 0) |
|---|
| 471 | + return irq; |
|---|
| 472 | 472 | |
|---|
| 473 | 473 | err = request_irq(irq, vm_interrupt, IRQF_SHARED, |
|---|
| 474 | 474 | dev_name(&vdev->dev), vm_dev); |
|---|
| .. | .. |
|---|
| 476 | 476 | return err; |
|---|
| 477 | 477 | |
|---|
| 478 | 478 | for (i = 0; i < nvqs; ++i) { |
|---|
| 479 | | - vqs[i] = vm_setup_vq(vdev, i, callbacks[i], names[i], |
|---|
| 479 | + if (!names[i]) { |
|---|
| 480 | + vqs[i] = NULL; |
|---|
| 481 | + continue; |
|---|
| 482 | + } |
|---|
| 483 | + |
|---|
| 484 | + vqs[i] = vm_setup_vq(vdev, queue_idx++, callbacks[i], names[i], |
|---|
| 480 | 485 | ctx ? ctx[i] : false); |
|---|
| 481 | 486 | if (IS_ERR(vqs[i])) { |
|---|
| 482 | 487 | vm_del_vqs(vdev); |
|---|
| .. | .. |
|---|
| 494 | 499 | return vm_dev->pdev->name; |
|---|
| 495 | 500 | } |
|---|
| 496 | 501 | |
|---|
| 502 | +static bool vm_get_shm_region(struct virtio_device *vdev, |
|---|
| 503 | + struct virtio_shm_region *region, u8 id) |
|---|
| 504 | +{ |
|---|
| 505 | + struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); |
|---|
| 506 | + u64 len, addr; |
|---|
| 507 | + |
|---|
| 508 | + /* Select the region we're interested in */ |
|---|
| 509 | + writel(id, vm_dev->base + VIRTIO_MMIO_SHM_SEL); |
|---|
| 510 | + |
|---|
| 511 | + /* Read the region size */ |
|---|
| 512 | + len = (u64) readl(vm_dev->base + VIRTIO_MMIO_SHM_LEN_LOW); |
|---|
| 513 | + len |= (u64) readl(vm_dev->base + VIRTIO_MMIO_SHM_LEN_HIGH) << 32; |
|---|
| 514 | + |
|---|
| 515 | + region->len = len; |
|---|
| 516 | + |
|---|
| 517 | + /* Check if region length is -1. If that's the case, the shared memory |
|---|
| 518 | + * region does not exist and there is no need to proceed further. |
|---|
| 519 | + */ |
|---|
| 520 | + if (len == ~(u64)0) |
|---|
| 521 | + return false; |
|---|
| 522 | + |
|---|
| 523 | + /* Read the region base address */ |
|---|
| 524 | + addr = (u64) readl(vm_dev->base + VIRTIO_MMIO_SHM_BASE_LOW); |
|---|
| 525 | + addr |= (u64) readl(vm_dev->base + VIRTIO_MMIO_SHM_BASE_HIGH) << 32; |
|---|
| 526 | + |
|---|
| 527 | + region->addr = addr; |
|---|
| 528 | + |
|---|
| 529 | + return true; |
|---|
| 530 | +} |
|---|
| 531 | + |
|---|
| 497 | 532 | static const struct virtio_config_ops virtio_mmio_config_ops = { |
|---|
| 498 | 533 | .get = vm_get, |
|---|
| 499 | 534 | .set = vm_set, |
|---|
| .. | .. |
|---|
| 506 | 541 | .get_features = vm_get_features, |
|---|
| 507 | 542 | .finalize_features = vm_finalize_features, |
|---|
| 508 | 543 | .bus_name = vm_bus_name, |
|---|
| 544 | + .get_shm_region = vm_get_shm_region, |
|---|
| 509 | 545 | }; |
|---|
| 510 | 546 | |
|---|
| 547 | +#ifdef CONFIG_PM_SLEEP |
|---|
| 548 | +static int virtio_mmio_freeze(struct device *dev) |
|---|
| 549 | +{ |
|---|
| 550 | + struct virtio_mmio_device *vm_dev = dev_get_drvdata(dev); |
|---|
| 551 | + |
|---|
| 552 | + return virtio_device_freeze(&vm_dev->vdev); |
|---|
| 553 | +} |
|---|
| 554 | + |
|---|
| 555 | +static int virtio_mmio_restore(struct device *dev) |
|---|
| 556 | +{ |
|---|
| 557 | + struct virtio_mmio_device *vm_dev = dev_get_drvdata(dev); |
|---|
| 558 | + |
|---|
| 559 | + if (vm_dev->version == 1) |
|---|
| 560 | + writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_GUEST_PAGE_SIZE); |
|---|
| 561 | + |
|---|
| 562 | + return virtio_device_restore(&vm_dev->vdev); |
|---|
| 563 | +} |
|---|
| 564 | + |
|---|
| 565 | +static const struct dev_pm_ops virtio_mmio_pm_ops = { |
|---|
| 566 | + SET_SYSTEM_SLEEP_PM_OPS(virtio_mmio_freeze, virtio_mmio_restore) |
|---|
| 567 | +}; |
|---|
| 568 | +#endif |
|---|
| 511 | 569 | |
|---|
| 512 | 570 | static void virtio_mmio_release_dev(struct device *_d) |
|---|
| 513 | 571 | { |
|---|
| 514 | 572 | struct virtio_device *vdev = |
|---|
| 515 | 573 | container_of(_d, struct virtio_device, dev); |
|---|
| 516 | | - struct virtio_mmio_device *vm_dev = |
|---|
| 517 | | - container_of(vdev, struct virtio_mmio_device, vdev); |
|---|
| 518 | | - struct platform_device *pdev = vm_dev->pdev; |
|---|
| 574 | + struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); |
|---|
| 519 | 575 | |
|---|
| 520 | | - devm_kfree(&pdev->dev, vm_dev); |
|---|
| 576 | + kfree(vm_dev); |
|---|
| 521 | 577 | } |
|---|
| 522 | 578 | |
|---|
| 523 | 579 | /* Platform device */ |
|---|
| .. | .. |
|---|
| 525 | 581 | static int virtio_mmio_probe(struct platform_device *pdev) |
|---|
| 526 | 582 | { |
|---|
| 527 | 583 | struct virtio_mmio_device *vm_dev; |
|---|
| 528 | | - struct resource *mem; |
|---|
| 529 | 584 | unsigned long magic; |
|---|
| 530 | 585 | int rc; |
|---|
| 531 | 586 | |
|---|
| 532 | | - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
|---|
| 533 | | - if (!mem) |
|---|
| 534 | | - return -EINVAL; |
|---|
| 535 | | - |
|---|
| 536 | | - if (!devm_request_mem_region(&pdev->dev, mem->start, |
|---|
| 537 | | - resource_size(mem), pdev->name)) |
|---|
| 538 | | - return -EBUSY; |
|---|
| 539 | | - |
|---|
| 540 | | - vm_dev = devm_kzalloc(&pdev->dev, sizeof(*vm_dev), GFP_KERNEL); |
|---|
| 587 | + vm_dev = kzalloc(sizeof(*vm_dev), GFP_KERNEL); |
|---|
| 541 | 588 | if (!vm_dev) |
|---|
| 542 | 589 | return -ENOMEM; |
|---|
| 543 | 590 | |
|---|
| .. | .. |
|---|
| 548 | 595 | INIT_LIST_HEAD(&vm_dev->virtqueues); |
|---|
| 549 | 596 | spin_lock_init(&vm_dev->lock); |
|---|
| 550 | 597 | |
|---|
| 551 | | - vm_dev->base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); |
|---|
| 552 | | - if (vm_dev->base == NULL) |
|---|
| 553 | | - return -EFAULT; |
|---|
| 598 | + vm_dev->base = devm_platform_ioremap_resource(pdev, 0); |
|---|
| 599 | + if (IS_ERR(vm_dev->base)) |
|---|
| 600 | + return PTR_ERR(vm_dev->base); |
|---|
| 554 | 601 | |
|---|
| 555 | 602 | /* Check magic value */ |
|---|
| 556 | 603 | magic = readl(vm_dev->base + VIRTIO_MMIO_MAGIC_VALUE); |
|---|
| .. | .. |
|---|
| 646 | 693 | &vm_cmdline_id, &consumed); |
|---|
| 647 | 694 | |
|---|
| 648 | 695 | /* |
|---|
| 649 | | - * sscanf() must processes at least 2 chunks; also there |
|---|
| 696 | + * sscanf() must process at least 2 chunks; also there |
|---|
| 650 | 697 | * must be no extra characters after the last chunk, so |
|---|
| 651 | 698 | * str[consumed] must be '\0' |
|---|
| 652 | 699 | */ |
|---|
| 653 | | - if (processed < 2 || str[consumed]) |
|---|
| 700 | + if (processed < 2 || str[consumed] || irq == 0) |
|---|
| 654 | 701 | return -EINVAL; |
|---|
| 655 | 702 | |
|---|
| 656 | 703 | resources[0].flags = IORESOURCE_MEM; |
|---|
| .. | .. |
|---|
| 663 | 710 | if (!vm_cmdline_parent_registered) { |
|---|
| 664 | 711 | err = device_register(&vm_cmdline_parent); |
|---|
| 665 | 712 | if (err) { |
|---|
| 713 | + put_device(&vm_cmdline_parent); |
|---|
| 666 | 714 | pr_err("Failed to register parent device!\n"); |
|---|
| 667 | 715 | return err; |
|---|
| 668 | 716 | } |
|---|
| .. | .. |
|---|
| 760 | 808 | .name = "virtio-mmio", |
|---|
| 761 | 809 | .of_match_table = virtio_mmio_match, |
|---|
| 762 | 810 | .acpi_match_table = ACPI_PTR(virtio_mmio_acpi_match), |
|---|
| 811 | +#ifdef CONFIG_PM_SLEEP |
|---|
| 812 | + .pm = &virtio_mmio_pm_ops, |
|---|
| 813 | +#endif |
|---|
| 763 | 814 | }, |
|---|
| 764 | 815 | }; |
|---|
| 765 | 816 | |
|---|