diff options
| author | Jinyoung Park <jinyoungp@nvidia.com> | 2018-09-14 09:24:19 -0400 |
|---|---|---|
| committer | mobile promotions <svcmobile_promotions@nvidia.com> | 2018-09-18 02:41:31 -0400 |
| commit | 4981200dfd24ef1f92cdc77f0641dec9b0f4e0b5 (patch) | |
| tree | 68d3abe69007cf0aa74cc851e12cb556f0ef2080 /drivers/virt | |
| parent | 29065935d9e023891ec5664120f5ef753b0cfed3 (diff) | |
virt: tegra: tegra_hv_pm_ctl: Add dependency management on System suspend
For dependency management on System suspend, the privileged guest sends
a guest suspend command to the listed guests from the "wait-for-guests"
DT property and waits for the guests to be suspended.
Jira STR-575
Change-Id: I47e7341bdc8b01a2ec3cb779184aa4477bc79931
Signed-off-by: Jinyoung Park <jinyoungp@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/1822897
GVS: Gerrit_Virtual_Submit
Reviewed-by: Sang-Hun Lee <sanlee@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
Diffstat (limited to 'drivers/virt')
| -rw-r--r-- | drivers/virt/tegra/tegra_hv_pm_ctl.c | 127 |
1 files changed, 125 insertions, 2 deletions
diff --git a/drivers/virt/tegra/tegra_hv_pm_ctl.c b/drivers/virt/tegra/tegra_hv_pm_ctl.c index 74458e9dc..82ffe994a 100644 --- a/drivers/virt/tegra/tegra_hv_pm_ctl.c +++ b/drivers/virt/tegra/tegra_hv_pm_ctl.c | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | #include <linux/interrupt.h> | 20 | #include <linux/interrupt.h> |
| 21 | #include <linux/cdev.h> | 21 | #include <linux/cdev.h> |
| 22 | #include <linux/poll.h> | 22 | #include <linux/poll.h> |
| 23 | #include <linux/delay.h> | ||
| 23 | 24 | ||
| 24 | #include <linux/tegra-ivc.h> | 25 | #include <linux/tegra-ivc.h> |
| 25 | #include <linux/tegra-ivc-instance.h> | 26 | #include <linux/tegra-ivc-instance.h> |
| @@ -32,6 +33,7 @@ | |||
| 32 | 33 | ||
| 33 | #define DRV_NAME "tegra_hv_pm_ctl" | 34 | #define DRV_NAME "tegra_hv_pm_ctl" |
| 34 | #define CHAR_DEV_COUNT 1 | 35 | #define CHAR_DEV_COUNT 1 |
| 36 | #define MAX_GUESTS_NUM 8 | ||
| 35 | 37 | ||
| 36 | struct tegra_hv_pm_ctl { | 38 | struct tegra_hv_pm_ctl { |
| 37 | struct device *dev; | 39 | struct device *dev; |
| @@ -43,6 +45,8 @@ struct tegra_hv_pm_ctl { | |||
| 43 | struct cdev cdev; | 45 | struct cdev cdev; |
| 44 | dev_t char_devt; | 46 | dev_t char_devt; |
| 45 | bool char_is_open; | 47 | bool char_is_open; |
| 48 | u32 wait_for_guests[MAX_GUESTS_NUM]; | ||
| 49 | u32 wait_for_guests_size; | ||
| 46 | 50 | ||
| 47 | struct mutex mutex_lock; | 51 | struct mutex mutex_lock; |
| 48 | wait_queue_head_t wq; | 52 | wait_queue_head_t wq; |
| @@ -50,13 +54,72 @@ struct tegra_hv_pm_ctl { | |||
| 50 | 54 | ||
| 51 | int (*tegra_hv_pm_ctl_prepare_shutdown)(void); | 55 | int (*tegra_hv_pm_ctl_prepare_shutdown)(void); |
| 52 | 56 | ||
| 57 | /* Global driver data */ | ||
| 58 | static struct tegra_hv_pm_ctl *tegra_hv_pm_ctl_data; | ||
| 59 | |||
| 53 | /* Guest ID for state */ | 60 | /* Guest ID for state */ |
| 54 | static u32 guest_id; | 61 | static u32 guest_id; |
| 55 | 62 | ||
| 63 | static int tegra_hv_pm_ctl_get_guest_state(u32 vmid, u32 *state); | ||
| 64 | |||
| 65 | /* | ||
| 66 | * For dependency management on System suspend, if there are guests required | ||
| 67 | * to wait and the guests are not in suspended, the privileged guest sends | ||
| 68 | * a guest suspend command to the guests and waits for the guests to be | ||
| 69 | * suspended. | ||
| 70 | */ | ||
| 71 | static int do_wait_for_guests_suspended(void) | ||
| 72 | { | ||
| 73 | bool sent_guest_suspend = false; | ||
| 74 | int i = 0; | ||
| 75 | int ret = 0; | ||
| 76 | |||
| 77 | while (i < tegra_hv_pm_ctl_data->wait_for_guests_size) { | ||
| 78 | u32 vmid = tegra_hv_pm_ctl_data->wait_for_guests[i]; | ||
| 79 | u32 state; | ||
| 80 | |||
| 81 | ret = tegra_hv_pm_ctl_get_guest_state(vmid, &state); | ||
| 82 | if (ret < 0) | ||
| 83 | return ret; | ||
| 84 | |||
| 85 | if (state == VM_STATE_SUSPEND) { | ||
| 86 | sent_guest_suspend = false; | ||
| 87 | i++; | ||
| 88 | continue; | ||
| 89 | } | ||
| 90 | |||
| 91 | if (sent_guest_suspend == false) { | ||
| 92 | pr_debug("%s: Send a guest suspend command to guest%u\n", | ||
| 93 | __func__, vmid); | ||
| 94 | ret = tegra_hv_pm_ctl_trigger_guest_suspend(vmid); | ||
| 95 | if (ret < 0) | ||
| 96 | return ret; | ||
| 97 | |||
| 98 | sent_guest_suspend = true; | ||
| 99 | } | ||
| 100 | msleep(10); | ||
| 101 | } | ||
| 102 | |||
| 103 | return 0; | ||
| 104 | } | ||
| 105 | |||
| 56 | int tegra_hv_pm_ctl_trigger_sys_suspend(void) | 106 | int tegra_hv_pm_ctl_trigger_sys_suspend(void) |
| 57 | { | 107 | { |
| 58 | int ret; | 108 | int ret; |
| 59 | 109 | ||
| 110 | if (!tegra_hv_pm_ctl_data) { | ||
| 111 | pr_err("%s: tegra_hv_pm_ctl driver is not probed, %d\n", | ||
| 112 | __func__, -ENXIO); | ||
| 113 | return -ENXIO; | ||
| 114 | } | ||
| 115 | |||
| 116 | ret = do_wait_for_guests_suspended(); | ||
| 117 | if (ret < 0) { | ||
| 118 | pr_err("%s: Failed to wait for guests suspended, %d\n", | ||
| 119 | __func__, ret); | ||
| 120 | return ret; | ||
| 121 | } | ||
| 122 | |||
| 60 | ret = hyp_guest_reset(SYS_SUSPEND_INIT_CMD, NULL); | 123 | ret = hyp_guest_reset(SYS_SUSPEND_INIT_CMD, NULL); |
| 61 | if (ret < 0) { | 124 | if (ret < 0) { |
| 62 | pr_err("%s: Failed to trigger system suspend, %d\n", | 125 | pr_err("%s: Failed to trigger system suspend, %d\n", |
| @@ -67,11 +130,16 @@ int tegra_hv_pm_ctl_trigger_sys_suspend(void) | |||
| 67 | return 0; | 130 | return 0; |
| 68 | } | 131 | } |
| 69 | 132 | ||
| 70 | |||
| 71 | int tegra_hv_pm_ctl_trigger_sys_shutdown(void) | 133 | int tegra_hv_pm_ctl_trigger_sys_shutdown(void) |
| 72 | { | 134 | { |
| 73 | int ret; | 135 | int ret; |
| 74 | 136 | ||
| 137 | if (!tegra_hv_pm_ctl_data) { | ||
| 138 | pr_err("%s: tegra_hv_pm_ctl driver is not probed, %d\n", | ||
| 139 | __func__, -ENXIO); | ||
| 140 | return -ENXIO; | ||
| 141 | } | ||
| 142 | |||
| 75 | if (tegra_hv_pm_ctl_prepare_shutdown) { | 143 | if (tegra_hv_pm_ctl_prepare_shutdown) { |
| 76 | ret = tegra_hv_pm_ctl_prepare_shutdown(); | 144 | ret = tegra_hv_pm_ctl_prepare_shutdown(); |
| 77 | if (ret < 0) { | 145 | if (ret < 0) { |
| @@ -95,6 +163,12 @@ int tegra_hv_pm_ctl_trigger_sys_reboot(void) | |||
| 95 | { | 163 | { |
| 96 | int ret; | 164 | int ret; |
| 97 | 165 | ||
| 166 | if (!tegra_hv_pm_ctl_data) { | ||
| 167 | pr_err("%s: tegra_hv_pm_ctl driver is not probed, %d\n", | ||
| 168 | __func__, -ENXIO); | ||
| 169 | return -ENXIO; | ||
| 170 | } | ||
| 171 | |||
| 98 | ret = hyp_guest_reset(SYS_REBOOT_INIT_CMD, NULL); | 172 | ret = hyp_guest_reset(SYS_REBOOT_INIT_CMD, NULL); |
| 99 | if (ret < 0) { | 173 | if (ret < 0) { |
| 100 | pr_err("%s: Failed to trigger system reboot, %d\n", | 174 | pr_err("%s: Failed to trigger system reboot, %d\n", |
| @@ -109,6 +183,12 @@ int tegra_hv_pm_ctl_trigger_guest_suspend(u32 vmid) | |||
| 109 | { | 183 | { |
| 110 | int ret; | 184 | int ret; |
| 111 | 185 | ||
| 186 | if (!tegra_hv_pm_ctl_data) { | ||
| 187 | pr_err("%s: tegra_hv_pm_ctl driver is not probed, %d\n", | ||
| 188 | __func__, -ENXIO); | ||
| 189 | return -ENXIO; | ||
| 190 | } | ||
| 191 | |||
| 112 | ret = hyp_guest_reset(GUEST_SUSPEND_REQ_CMD(vmid), NULL); | 192 | ret = hyp_guest_reset(GUEST_SUSPEND_REQ_CMD(vmid), NULL); |
| 113 | if (ret < 0) { | 193 | if (ret < 0) { |
| 114 | pr_err("%s: Failed to trigger guest%u suspend, %d\n", | 194 | pr_err("%s: Failed to trigger guest%u suspend, %d\n", |
| @@ -123,6 +203,12 @@ int tegra_hv_pm_ctl_trigger_guest_resume(u32 vmid) | |||
| 123 | { | 203 | { |
| 124 | int ret; | 204 | int ret; |
| 125 | 205 | ||
| 206 | if (!tegra_hv_pm_ctl_data) { | ||
| 207 | pr_err("%s: tegra_hv_pm_ctl driver is not probed, %d\n", | ||
| 208 | __func__, -ENXIO); | ||
| 209 | return -ENXIO; | ||
| 210 | } | ||
| 211 | |||
| 126 | ret = hyp_guest_reset(GUEST_RESUME_INIT_CMD(vmid), NULL); | 212 | ret = hyp_guest_reset(GUEST_RESUME_INIT_CMD(vmid), NULL); |
| 127 | if (ret < 0) { | 213 | if (ret < 0) { |
| 128 | pr_err("%s: Failed to trigger guest%u resume, %d\n", | 214 | pr_err("%s: Failed to trigger guest%u resume, %d\n", |
| @@ -133,10 +219,18 @@ int tegra_hv_pm_ctl_trigger_guest_resume(u32 vmid) | |||
| 133 | return 0; | 219 | return 0; |
| 134 | } | 220 | } |
| 135 | 221 | ||
| 136 | int tegra_hv_pm_ctl_get_guest_state(u32 vmid, u32 *state) | 222 | static int tegra_hv_pm_ctl_get_guest_state(u32 vmid, u32 *state) |
| 137 | { | 223 | { |
| 138 | int ret; | 224 | int ret; |
| 139 | 225 | ||
| 226 | if (!tegra_hv_pm_ctl_data) { | ||
| 227 | pr_err("%s: tegra_hv_pm_ctl driver is not probed, %d\n", | ||
| 228 | __func__, -ENXIO); | ||
| 229 | return -ENXIO; | ||
| 230 | } | ||
| 231 | |||
| 232 | /* guest state which can be returned: | ||
| 233 | * VM_STATE_BOOT, VM_STATE_HALT, VM_STATE_SUSPEND, VM_STATE_SHUTDOWN */ | ||
| 140 | ret = hyp_read_guest_state(vmid, state); | 234 | ret = hyp_read_guest_state(vmid, state); |
| 141 | if (ret < 0) { | 235 | if (ret < 0) { |
| 142 | pr_err("%s: Failed to get guest%u state, %d\n", | 236 | pr_err("%s: Failed to get guest%u state, %d\n", |
| @@ -492,6 +586,22 @@ static ssize_t guest_state_store(struct device *dev, | |||
| 492 | return count; | 586 | return count; |
| 493 | } | 587 | } |
| 494 | 588 | ||
| 589 | static ssize_t wait_for_guests_show(struct device *dev, | ||
| 590 | struct device_attribute *attr, char *buf) | ||
| 591 | { | ||
| 592 | struct tegra_hv_pm_ctl *data = dev_get_drvdata(dev); | ||
| 593 | ssize_t count = 0; | ||
| 594 | int i; | ||
| 595 | |||
| 596 | for (i = 0; i < data->wait_for_guests_size; i++) { | ||
| 597 | count += snprintf(buf + count, PAGE_SIZE - count, "%u ", | ||
| 598 | data->wait_for_guests[i]); | ||
| 599 | } | ||
| 600 | count += snprintf(buf + count, PAGE_SIZE - count, "\n"); | ||
| 601 | |||
| 602 | return count; | ||
| 603 | } | ||
| 604 | |||
| 495 | static DEVICE_ATTR_RO(ivc_id); | 605 | static DEVICE_ATTR_RO(ivc_id); |
| 496 | static DEVICE_ATTR_RO(ivc_frame_size); | 606 | static DEVICE_ATTR_RO(ivc_frame_size); |
| 497 | static DEVICE_ATTR_RO(ivc_nframes); | 607 | static DEVICE_ATTR_RO(ivc_nframes); |
| @@ -502,6 +612,7 @@ static DEVICE_ATTR_WO(trigger_sys_reboot); | |||
| 502 | static DEVICE_ATTR_WO(trigger_guest_suspend); | 612 | static DEVICE_ATTR_WO(trigger_guest_suspend); |
| 503 | static DEVICE_ATTR_WO(trigger_guest_resume); | 613 | static DEVICE_ATTR_WO(trigger_guest_resume); |
| 504 | static DEVICE_ATTR_RW(guest_state); | 614 | static DEVICE_ATTR_RW(guest_state); |
| 615 | static DEVICE_ATTR_RO(wait_for_guests); | ||
| 505 | 616 | ||
| 506 | static struct attribute *tegra_hv_pm_ctl_attributes[] = { | 617 | static struct attribute *tegra_hv_pm_ctl_attributes[] = { |
| 507 | &dev_attr_ivc_id.attr, | 618 | &dev_attr_ivc_id.attr, |
| @@ -514,6 +625,7 @@ static struct attribute *tegra_hv_pm_ctl_attributes[] = { | |||
| 514 | &dev_attr_trigger_guest_suspend.attr, | 625 | &dev_attr_trigger_guest_suspend.attr, |
| 515 | &dev_attr_trigger_guest_resume.attr, | 626 | &dev_attr_trigger_guest_resume.attr, |
| 516 | &dev_attr_guest_state.attr, | 627 | &dev_attr_guest_state.attr, |
| 628 | &dev_attr_wait_for_guests.attr, | ||
| 517 | NULL | 629 | NULL |
| 518 | }; | 630 | }; |
| 519 | 631 | ||
| @@ -600,6 +712,7 @@ static void tegra_hv_pm_ctl_cleanup(struct tegra_hv_pm_ctl *data) | |||
| 600 | static int tegra_hv_pm_ctl_parse_dt(struct tegra_hv_pm_ctl *data) | 712 | static int tegra_hv_pm_ctl_parse_dt(struct tegra_hv_pm_ctl *data) |
| 601 | { | 713 | { |
| 602 | struct device_node *np = data->dev->of_node; | 714 | struct device_node *np = data->dev->of_node; |
| 715 | int ret; | ||
| 603 | 716 | ||
| 604 | if (!np) { | 717 | if (!np) { |
| 605 | dev_err(data->dev, "%s: Failed to find device node\n", | 718 | dev_err(data->dev, "%s: Failed to find device node\n", |
| @@ -613,6 +726,13 @@ static int tegra_hv_pm_ctl_parse_dt(struct tegra_hv_pm_ctl *data) | |||
| 613 | return -EINVAL; | 726 | return -EINVAL; |
| 614 | } | 727 | } |
| 615 | 728 | ||
| 729 | /* List of guests to wait before sending a System suspend command for | ||
| 730 | * dependency management. */ | ||
| 731 | ret = of_property_read_variable_u32_array(np, "wait-for-guests", | ||
| 732 | data->wait_for_guests, 1, MAX_GUESTS_NUM); | ||
| 733 | if (ret > 0) | ||
| 734 | data->wait_for_guests_size = ret; | ||
| 735 | |||
| 616 | return 0; | 736 | return 0; |
| 617 | } | 737 | } |
| 618 | 738 | ||
| @@ -669,6 +789,8 @@ static int tegra_hv_pm_ctl_probe(struct platform_device *pdev) | |||
| 669 | goto error_sysfs_remove_group; | 789 | goto error_sysfs_remove_group; |
| 670 | } | 790 | } |
| 671 | 791 | ||
| 792 | tegra_hv_pm_ctl_data = data; | ||
| 793 | |||
| 672 | dev_info(&pdev->dev, "%s: Probed\n", __func__); | 794 | dev_info(&pdev->dev, "%s: Probed\n", __func__); |
| 673 | 795 | ||
| 674 | return 0; | 796 | return 0; |
| @@ -685,6 +807,7 @@ static int tegra_hv_pm_ctl_remove(struct platform_device *pdev) | |||
| 685 | { | 807 | { |
| 686 | struct tegra_hv_pm_ctl *data = platform_get_drvdata(pdev); | 808 | struct tegra_hv_pm_ctl *data = platform_get_drvdata(pdev); |
| 687 | 809 | ||
| 810 | tegra_hv_pm_ctl_data = NULL; | ||
| 688 | tegra_hv_pm_ctl_cleanup(data); | 811 | tegra_hv_pm_ctl_cleanup(data); |
| 689 | sysfs_remove_group(&pdev->dev.kobj, &tegra_hv_pm_ctl_attr_group); | 812 | sysfs_remove_group(&pdev->dev.kobj, &tegra_hv_pm_ctl_attr_group); |
| 690 | class_destroy(data->class); | 813 | class_destroy(data->class); |
