vfio: platform: call _RST method when using ACPI
authorSinan Kaya <okaya@codeaurora.org>
Tue, 19 Jul 2016 13:01:46 +0000 (09:01 -0400)
committerAlex Williamson <alex.williamson@redhat.com>
Tue, 19 Jul 2016 16:26:44 +0000 (10:26 -0600)
The device tree code checks for the presence of a reset driver and calls
the of_reset function pointer by looking up the reset driver as a module.

ACPI defines _RST method to perform device level reset. After the _RST
method is executed, the OS can resume using the device. _RST method is
expected to stop DMA transfers and IRQs.

This patch introduces two functions as vfio_platform_acpi_has_reset and
vfio_platform_acpi_call_reset. The has reset method is used to declare
reset capability via the ioctl flag VFIO_DEVICE_FLAGS_RESET. The call
reset function is used to execute the _RST ACPI method.

Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
drivers/vfio/platform/vfio_platform_common.c

index a9760c2..a15a69b 100644 (file)
@@ -28,6 +28,8 @@
 #define DRIVER_AUTHOR   "Antonios Motakis <a.motakis@virtualopensystems.com>"
 #define DRIVER_DESC     "VFIO platform base module"
 
+#define VFIO_PLATFORM_IS_ACPI(vdev) ((vdev)->acpihid != NULL)
+
 static LIST_HEAD(reset_list);
 static DEFINE_MUTEX(driver_lock);
 
@@ -71,13 +73,53 @@ static int vfio_platform_acpi_probe(struct vfio_platform_device *vdev,
        return WARN_ON(!vdev->acpihid) ? -EINVAL : 0;
 }
 
+int vfio_platform_acpi_call_reset(struct vfio_platform_device *vdev,
+                                 const char **extra_dbg)
+{
+#ifdef CONFIG_ACPI
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+       struct device *dev = vdev->device;
+       acpi_handle handle = ACPI_HANDLE(dev);
+       acpi_status acpi_ret;
+
+       acpi_ret = acpi_evaluate_object(handle, "_RST", NULL, &buffer);
+       if (ACPI_FAILURE(acpi_ret)) {
+               if (extra_dbg)
+                       *extra_dbg = acpi_format_exception(acpi_ret);
+               return -EINVAL;
+       }
+
+       return 0;
+#else
+       return -ENOENT;
+#endif
+}
+
+bool vfio_platform_acpi_has_reset(struct vfio_platform_device *vdev)
+{
+#ifdef CONFIG_ACPI
+       struct device *dev = vdev->device;
+       acpi_handle handle = ACPI_HANDLE(dev);
+
+       return acpi_has_method(handle, "_RST");
+#else
+       return false;
+#endif
+}
+
 static bool vfio_platform_has_reset(struct vfio_platform_device *vdev)
 {
+       if (VFIO_PLATFORM_IS_ACPI(vdev))
+               return vfio_platform_acpi_has_reset(vdev);
+
        return vdev->of_reset ? true : false;
 }
 
 static void vfio_platform_get_reset(struct vfio_platform_device *vdev)
 {
+       if (VFIO_PLATFORM_IS_ACPI(vdev))
+               return;
+
        vdev->of_reset = vfio_platform_lookup_reset(vdev->compat,
                                                    &vdev->reset_module);
        if (!vdev->of_reset) {
@@ -89,6 +131,9 @@ static void vfio_platform_get_reset(struct vfio_platform_device *vdev)
 
 static void vfio_platform_put_reset(struct vfio_platform_device *vdev)
 {
+       if (VFIO_PLATFORM_IS_ACPI(vdev))
+               return;
+
        if (vdev->of_reset)
                module_put(vdev->reset_module);
 }
@@ -164,7 +209,10 @@ static void vfio_platform_regions_cleanup(struct vfio_platform_device *vdev)
 static int vfio_platform_call_reset(struct vfio_platform_device *vdev,
                                    const char **extra_dbg)
 {
-       if (vdev->of_reset) {
+       if (VFIO_PLATFORM_IS_ACPI(vdev)) {
+               dev_info(vdev->device, "reset\n");
+               return vfio_platform_acpi_call_reset(vdev, extra_dbg);
+       } else if (vdev->of_reset) {
                dev_info(vdev->device, "reset\n");
                return vdev->of_reset(vdev);
        }