Commit d23b9b00cdde5c93b914a172cecd57d5625fcd04
1 parent
ecf00475f2
Exists in
master
and in
6 other branches
PM / Domains: Rework system suspend callback routines (v2)
The current generic PM domains code attempts to use the generic system suspend operations along with the domains' device stop/start routines, which requires device drivers to assume that their system suspend/resume (and hibernation/restore) callbacks will always be used with generic PM domains. However, in theory, the same hardware may be used in devices that don't belong to any PM domain, in which case it would be necessary to add "fake" PM domains to satisfy the above assumption. Also, the domain the hardware belongs to may not be handled with the help of the generic code. To allow device drivers that may be used along with the generic PM domains code of more flexibility, add new device callbacks, .suspend(), .suspend_late(), .resume_early(), .resume(), .freeze(), .freeze_late(), .thaw_early(), and .thaw(), that can be supplied by the drivers in addition to their "standard" system suspend and hibernation callbacks. These new callbacks, if defined, will be used by the generic PM domains code for the handling of system suspend and hibernation instead of the "standard" ones. This will allow drivers to be designed to work with generic PM domains as well as without them. For backwards compatibility, introduce default implementations of the new callbacks for PM domains that will execute pm_generic_suspend(), pm_generic_suspend_noirq(), pm_generic_resume_noirq(), pm_generic_resume(), pm_generic_freeze(), pm_generic_freeze_noirq(), pm_generic_thaw_noirq(), and pm_generic_thaw(), respectively, for the given device if its driver doesn't define those callbacks. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Showing 2 changed files with 158 additions and 99 deletions Side-by-side Diff
drivers/base/power/domain.c
... | ... | @@ -561,6 +561,46 @@ |
561 | 561 | return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev); |
562 | 562 | } |
563 | 563 | |
564 | +static int genpd_suspend_dev(struct generic_pm_domain *genpd, struct device *dev) | |
565 | +{ | |
566 | + return GENPD_DEV_CALLBACK(genpd, int, suspend, dev); | |
567 | +} | |
568 | + | |
569 | +static int genpd_suspend_late(struct generic_pm_domain *genpd, struct device *dev) | |
570 | +{ | |
571 | + return GENPD_DEV_CALLBACK(genpd, int, suspend_late, dev); | |
572 | +} | |
573 | + | |
574 | +static int genpd_resume_early(struct generic_pm_domain *genpd, struct device *dev) | |
575 | +{ | |
576 | + return GENPD_DEV_CALLBACK(genpd, int, resume_early, dev); | |
577 | +} | |
578 | + | |
579 | +static int genpd_resume_dev(struct generic_pm_domain *genpd, struct device *dev) | |
580 | +{ | |
581 | + return GENPD_DEV_CALLBACK(genpd, int, resume, dev); | |
582 | +} | |
583 | + | |
584 | +static int genpd_freeze_dev(struct generic_pm_domain *genpd, struct device *dev) | |
585 | +{ | |
586 | + return GENPD_DEV_CALLBACK(genpd, int, freeze, dev); | |
587 | +} | |
588 | + | |
589 | +static int genpd_freeze_late(struct generic_pm_domain *genpd, struct device *dev) | |
590 | +{ | |
591 | + return GENPD_DEV_CALLBACK(genpd, int, freeze_late, dev); | |
592 | +} | |
593 | + | |
594 | +static int genpd_thaw_early(struct generic_pm_domain *genpd, struct device *dev) | |
595 | +{ | |
596 | + return GENPD_DEV_CALLBACK(genpd, int, thaw_early, dev); | |
597 | +} | |
598 | + | |
599 | +static int genpd_thaw_dev(struct generic_pm_domain *genpd, struct device *dev) | |
600 | +{ | |
601 | + return GENPD_DEV_CALLBACK(genpd, int, thaw, dev); | |
602 | +} | |
603 | + | |
564 | 604 | /** |
565 | 605 | * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters. |
566 | 606 | * @genpd: PM domain to power off, if possible. |
... | ... | @@ -712,7 +752,7 @@ |
712 | 752 | if (IS_ERR(genpd)) |
713 | 753 | return -EINVAL; |
714 | 754 | |
715 | - return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev); | |
755 | + return genpd->suspend_power_off ? 0 : genpd_suspend_dev(genpd, dev); | |
716 | 756 | } |
717 | 757 | |
718 | 758 | /** |
... | ... | @@ -737,7 +777,7 @@ |
737 | 777 | if (genpd->suspend_power_off) |
738 | 778 | return 0; |
739 | 779 | |
740 | - ret = pm_generic_suspend_noirq(dev); | |
780 | + ret = genpd_suspend_late(genpd, dev); | |
741 | 781 | if (ret) |
742 | 782 | return ret; |
743 | 783 | |
... | ... | @@ -788,7 +828,7 @@ |
788 | 828 | genpd->suspended_count--; |
789 | 829 | genpd_start_dev(genpd, dev); |
790 | 830 | |
791 | - return pm_generic_resume_noirq(dev); | |
831 | + return genpd_resume_early(genpd, dev); | |
792 | 832 | } |
793 | 833 | |
794 | 834 | /** |
... | ... | @@ -809,7 +849,7 @@ |
809 | 849 | if (IS_ERR(genpd)) |
810 | 850 | return -EINVAL; |
811 | 851 | |
812 | - return genpd->suspend_power_off ? 0 : pm_generic_resume(dev); | |
852 | + return genpd->suspend_power_off ? 0 : genpd_resume_dev(genpd, dev); | |
813 | 853 | } |
814 | 854 | |
815 | 855 | /** |
... | ... | @@ -830,7 +870,7 @@ |
830 | 870 | if (IS_ERR(genpd)) |
831 | 871 | return -EINVAL; |
832 | 872 | |
833 | - return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev); | |
873 | + return genpd->suspend_power_off ? 0 : genpd_freeze_dev(genpd, dev); | |
834 | 874 | } |
835 | 875 | |
836 | 876 | /** |
... | ... | @@ -856,7 +896,7 @@ |
856 | 896 | if (genpd->suspend_power_off) |
857 | 897 | return 0; |
858 | 898 | |
859 | - ret = pm_generic_freeze_noirq(dev); | |
899 | + ret = genpd_freeze_late(genpd, dev); | |
860 | 900 | if (ret) |
861 | 901 | return ret; |
862 | 902 | |
... | ... | @@ -889,7 +929,7 @@ |
889 | 929 | |
890 | 930 | genpd_start_dev(genpd, dev); |
891 | 931 | |
892 | - return pm_generic_thaw_noirq(dev); | |
932 | + return genpd_thaw_early(genpd, dev); | |
893 | 933 | } |
894 | 934 | |
895 | 935 | /** |
896 | 936 | |
... | ... | @@ -910,73 +950,10 @@ |
910 | 950 | if (IS_ERR(genpd)) |
911 | 951 | return -EINVAL; |
912 | 952 | |
913 | - return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev); | |
953 | + return genpd->suspend_power_off ? 0 : genpd_thaw_dev(genpd, dev); | |
914 | 954 | } |
915 | 955 | |
916 | 956 | /** |
917 | - * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain. | |
918 | - * @dev: Device to suspend. | |
919 | - * | |
920 | - * Power off a device under the assumption that its pm_domain field points to | |
921 | - * the domain member of an object of type struct generic_pm_domain representing | |
922 | - * a PM domain consisting of I/O devices. | |
923 | - */ | |
924 | -static int pm_genpd_dev_poweroff(struct device *dev) | |
925 | -{ | |
926 | - struct generic_pm_domain *genpd; | |
927 | - | |
928 | - dev_dbg(dev, "%s()\n", __func__); | |
929 | - | |
930 | - genpd = dev_to_genpd(dev); | |
931 | - if (IS_ERR(genpd)) | |
932 | - return -EINVAL; | |
933 | - | |
934 | - return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev); | |
935 | -} | |
936 | - | |
937 | -/** | |
938 | - * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain. | |
939 | - * @dev: Device to suspend. | |
940 | - * | |
941 | - * Carry out a late powering off of a device under the assumption that its | |
942 | - * pm_domain field points to the domain member of an object of type | |
943 | - * struct generic_pm_domain representing a PM domain consisting of I/O devices. | |
944 | - */ | |
945 | -static int pm_genpd_dev_poweroff_noirq(struct device *dev) | |
946 | -{ | |
947 | - struct generic_pm_domain *genpd; | |
948 | - int ret; | |
949 | - | |
950 | - dev_dbg(dev, "%s()\n", __func__); | |
951 | - | |
952 | - genpd = dev_to_genpd(dev); | |
953 | - if (IS_ERR(genpd)) | |
954 | - return -EINVAL; | |
955 | - | |
956 | - if (genpd->suspend_power_off) | |
957 | - return 0; | |
958 | - | |
959 | - ret = pm_generic_poweroff_noirq(dev); | |
960 | - if (ret) | |
961 | - return ret; | |
962 | - | |
963 | - if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)) | |
964 | - return 0; | |
965 | - | |
966 | - genpd_stop_dev(genpd, dev); | |
967 | - | |
968 | - /* | |
969 | - * Since all of the "noirq" callbacks are executed sequentially, it is | |
970 | - * guaranteed that this function will never run twice in parallel for | |
971 | - * the same PM domain, so it is not necessary to use locking here. | |
972 | - */ | |
973 | - genpd->suspended_count++; | |
974 | - pm_genpd_sync_poweroff(genpd); | |
975 | - | |
976 | - return 0; | |
977 | -} | |
978 | - | |
979 | -/** | |
980 | 957 | * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain. |
981 | 958 | * @dev: Device to resume. |
982 | 959 | * |
983 | 960 | |
... | ... | @@ -1015,31 +992,10 @@ |
1015 | 992 | genpd->suspended_count--; |
1016 | 993 | genpd_start_dev(genpd, dev); |
1017 | 994 | |
1018 | - return pm_generic_restore_noirq(dev); | |
995 | + return genpd_resume_early(genpd, dev); | |
1019 | 996 | } |
1020 | 997 | |
1021 | 998 | /** |
1022 | - * pm_genpd_restore - Restore a device belonging to an I/O power domain. | |
1023 | - * @dev: Device to resume. | |
1024 | - * | |
1025 | - * Restore a device under the assumption that its pm_domain field points to the | |
1026 | - * domain member of an object of type struct generic_pm_domain representing | |
1027 | - * a power domain consisting of I/O devices. | |
1028 | - */ | |
1029 | -static int pm_genpd_restore(struct device *dev) | |
1030 | -{ | |
1031 | - struct generic_pm_domain *genpd; | |
1032 | - | |
1033 | - dev_dbg(dev, "%s()\n", __func__); | |
1034 | - | |
1035 | - genpd = dev_to_genpd(dev); | |
1036 | - if (IS_ERR(genpd)) | |
1037 | - return -EINVAL; | |
1038 | - | |
1039 | - return genpd->suspend_power_off ? 0 : pm_generic_restore(dev); | |
1040 | -} | |
1041 | - | |
1042 | -/** | |
1043 | 999 | * pm_genpd_complete - Complete power transition of a device in a power domain. |
1044 | 1000 | * @dev: Device to complete the transition of. |
1045 | 1001 | * |
1046 | 1002 | |
... | ... | @@ -1086,10 +1042,7 @@ |
1086 | 1042 | #define pm_genpd_freeze_noirq NULL |
1087 | 1043 | #define pm_genpd_thaw_noirq NULL |
1088 | 1044 | #define pm_genpd_thaw NULL |
1089 | -#define pm_genpd_dev_poweroff_noirq NULL | |
1090 | -#define pm_genpd_dev_poweroff NULL | |
1091 | 1045 | #define pm_genpd_restore_noirq NULL |
1092 | -#define pm_genpd_restore NULL | |
1093 | 1046 | #define pm_genpd_complete NULL |
1094 | 1047 | |
1095 | 1048 | #endif /* CONFIG_PM_SLEEP */ |
... | ... | @@ -1361,6 +1314,8 @@ |
1361 | 1314 | } |
1362 | 1315 | EXPORT_SYMBOL_GPL(pm_genpd_remove_callbacks); |
1363 | 1316 | |
1317 | +/* Default device callbacks for generic PM domains. */ | |
1318 | + | |
1364 | 1319 | /** |
1365 | 1320 | * pm_genpd_default_save_state - Default "save device state" for PM domians. |
1366 | 1321 | * @dev: Device to handle. |
... | ... | @@ -1400,6 +1355,94 @@ |
1400 | 1355 | } |
1401 | 1356 | |
1402 | 1357 | /** |
1358 | + * pm_genpd_default_suspend - Default "device suspend" for PM domians. | |
1359 | + * @dev: Device to handle. | |
1360 | + */ | |
1361 | +static int pm_genpd_default_suspend(struct device *dev) | |
1362 | +{ | |
1363 | + int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze; | |
1364 | + | |
1365 | + return cb ? cb(dev) : pm_generic_suspend(dev); | |
1366 | +} | |
1367 | + | |
1368 | +/** | |
1369 | + * pm_genpd_default_suspend_late - Default "late device suspend" for PM domians. | |
1370 | + * @dev: Device to handle. | |
1371 | + */ | |
1372 | +static int pm_genpd_default_suspend_late(struct device *dev) | |
1373 | +{ | |
1374 | + int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze_late; | |
1375 | + | |
1376 | + return cb ? cb(dev) : pm_generic_suspend_noirq(dev); | |
1377 | +} | |
1378 | + | |
1379 | +/** | |
1380 | + * pm_genpd_default_resume_early - Default "early device resume" for PM domians. | |
1381 | + * @dev: Device to handle. | |
1382 | + */ | |
1383 | +static int pm_genpd_default_resume_early(struct device *dev) | |
1384 | +{ | |
1385 | + int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw_early; | |
1386 | + | |
1387 | + return cb ? cb(dev) : pm_generic_resume_noirq(dev); | |
1388 | +} | |
1389 | + | |
1390 | +/** | |
1391 | + * pm_genpd_default_resume - Default "device resume" for PM domians. | |
1392 | + * @dev: Device to handle. | |
1393 | + */ | |
1394 | +static int pm_genpd_default_resume(struct device *dev) | |
1395 | +{ | |
1396 | + int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw; | |
1397 | + | |
1398 | + return cb ? cb(dev) : pm_generic_resume(dev); | |
1399 | +} | |
1400 | + | |
1401 | +/** | |
1402 | + * pm_genpd_default_freeze - Default "device freeze" for PM domians. | |
1403 | + * @dev: Device to handle. | |
1404 | + */ | |
1405 | +static int pm_genpd_default_freeze(struct device *dev) | |
1406 | +{ | |
1407 | + int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze; | |
1408 | + | |
1409 | + return cb ? cb(dev) : pm_generic_freeze(dev); | |
1410 | +} | |
1411 | + | |
1412 | +/** | |
1413 | + * pm_genpd_default_freeze_late - Default "late device freeze" for PM domians. | |
1414 | + * @dev: Device to handle. | |
1415 | + */ | |
1416 | +static int pm_genpd_default_freeze_late(struct device *dev) | |
1417 | +{ | |
1418 | + int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze_late; | |
1419 | + | |
1420 | + return cb ? cb(dev) : pm_generic_freeze_noirq(dev); | |
1421 | +} | |
1422 | + | |
1423 | +/** | |
1424 | + * pm_genpd_default_thaw_early - Default "early device thaw" for PM domians. | |
1425 | + * @dev: Device to handle. | |
1426 | + */ | |
1427 | +static int pm_genpd_default_thaw_early(struct device *dev) | |
1428 | +{ | |
1429 | + int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw_early; | |
1430 | + | |
1431 | + return cb ? cb(dev) : pm_generic_thaw_noirq(dev); | |
1432 | +} | |
1433 | + | |
1434 | +/** | |
1435 | + * pm_genpd_default_thaw - Default "device thaw" for PM domians. | |
1436 | + * @dev: Device to handle. | |
1437 | + */ | |
1438 | +static int pm_genpd_default_thaw(struct device *dev) | |
1439 | +{ | |
1440 | + int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw; | |
1441 | + | |
1442 | + return cb ? cb(dev) : pm_generic_thaw(dev); | |
1443 | +} | |
1444 | + | |
1445 | +/** | |
1403 | 1446 | * pm_genpd_init - Initialize a generic I/O PM domain object. |
1404 | 1447 | * @genpd: PM domain object to initialize. |
1405 | 1448 | * @gov: PM domain governor to associate with the domain (may be NULL). |
1406 | 1449 | |
1407 | 1450 | |
... | ... | @@ -1437,13 +1480,21 @@ |
1437 | 1480 | genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; |
1438 | 1481 | genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; |
1439 | 1482 | genpd->domain.ops.thaw = pm_genpd_thaw; |
1440 | - genpd->domain.ops.poweroff = pm_genpd_dev_poweroff; | |
1441 | - genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq; | |
1483 | + genpd->domain.ops.poweroff = pm_genpd_suspend; | |
1484 | + genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq; | |
1442 | 1485 | genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; |
1443 | - genpd->domain.ops.restore = pm_genpd_restore; | |
1486 | + genpd->domain.ops.restore = pm_genpd_resume; | |
1444 | 1487 | genpd->domain.ops.complete = pm_genpd_complete; |
1445 | 1488 | genpd->dev_ops.save_state = pm_genpd_default_save_state; |
1446 | 1489 | genpd->dev_ops.restore_state = pm_genpd_default_restore_state; |
1490 | + genpd->dev_ops.freeze = pm_genpd_default_suspend; | |
1491 | + genpd->dev_ops.freeze_late = pm_genpd_default_suspend_late; | |
1492 | + genpd->dev_ops.thaw_early = pm_genpd_default_resume_early; | |
1493 | + genpd->dev_ops.thaw = pm_genpd_default_resume; | |
1494 | + genpd->dev_ops.freeze = pm_genpd_default_freeze; | |
1495 | + genpd->dev_ops.freeze_late = pm_genpd_default_freeze_late; | |
1496 | + genpd->dev_ops.thaw_early = pm_genpd_default_thaw_early; | |
1497 | + genpd->dev_ops.thaw = pm_genpd_default_thaw; | |
1447 | 1498 | mutex_lock(&gpd_list_lock); |
1448 | 1499 | list_add(&genpd->gpd_list_node, &gpd_list); |
1449 | 1500 | mutex_unlock(&gpd_list_lock); |
include/linux/pm_domain.h
... | ... | @@ -28,6 +28,14 @@ |
28 | 28 | int (*stop)(struct device *dev); |
29 | 29 | int (*save_state)(struct device *dev); |
30 | 30 | int (*restore_state)(struct device *dev); |
31 | + int (*suspend)(struct device *dev); | |
32 | + int (*suspend_late)(struct device *dev); | |
33 | + int (*resume_early)(struct device *dev); | |
34 | + int (*resume)(struct device *dev); | |
35 | + int (*freeze)(struct device *dev); | |
36 | + int (*freeze_late)(struct device *dev); | |
37 | + int (*thaw_early)(struct device *dev); | |
38 | + int (*thaw)(struct device *dev); | |
31 | 39 | bool (*active_wakeup)(struct device *dev); |
32 | 40 | }; |
33 | 41 |