diff options
author | Sebastian Ott <sebott@linux.vnet.ibm.com> | 2009-06-16 04:30:22 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2009-06-16 04:31:09 -0400 |
commit | dcbd16d5111258df7c821ec1e4124fe6ffbf3c16 (patch) | |
tree | ee095cc2a4fc2a81bde256f8125b0199fe02a9ff /drivers/s390 | |
parent | 7e597a21a1470b12428cb0edd03c40986026451f (diff) |
[S390] pm: css bus power management callbacks
Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/cio/chsc.c | 3 | ||||
-rw-r--r-- | drivers/s390/cio/chsc.h | 1 | ||||
-rw-r--r-- | drivers/s390/cio/css.c | 157 | ||||
-rw-r--r-- | drivers/s390/cio/css.h | 10 |
4 files changed, 164 insertions, 7 deletions
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 883f16f96f22..1ecd3e567648 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c | |||
@@ -549,8 +549,7 @@ cleanup: | |||
549 | return ret; | 549 | return ret; |
550 | } | 550 | } |
551 | 551 | ||
552 | static int | 552 | int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) |
553 | __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) | ||
554 | { | 553 | { |
555 | struct { | 554 | struct { |
556 | struct chsc_header request; | 555 | struct chsc_header request; |
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index ba59bceace98..425e8f89a6c5 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h | |||
@@ -90,6 +90,7 @@ extern void chsc_free_sei_area(void); | |||
90 | extern int chsc_enable_facility(int); | 90 | extern int chsc_enable_facility(int); |
91 | struct channel_subsystem; | 91 | struct channel_subsystem; |
92 | extern int chsc_secm(struct channel_subsystem *, int); | 92 | extern int chsc_secm(struct channel_subsystem *, int); |
93 | int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page); | ||
93 | 94 | ||
94 | int chsc_chp_vary(struct chp_id chpid, int on); | 95 | int chsc_chp_vary(struct chp_id chpid, int on); |
95 | int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, | 96 | int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, |
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 0085d8901792..85d43c6bcb66 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c | |||
@@ -1,10 +1,10 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/s390/cio/css.c | 2 | * driver for channel subsystem |
3 | * driver for channel subsystem | ||
4 | * | 3 | * |
5 | * Copyright IBM Corp. 2002,2008 | 4 | * Copyright IBM Corp. 2002, 2009 |
6 | * Author(s): Arnd Bergmann (arndb@de.ibm.com) | 5 | * |
7 | * Cornelia Huck (cornelia.huck@de.ibm.com) | 6 | * Author(s): Arnd Bergmann (arndb@de.ibm.com) |
7 | * Cornelia Huck (cornelia.huck@de.ibm.com) | ||
8 | */ | 8 | */ |
9 | 9 | ||
10 | #define KMSG_COMPONENT "cio" | 10 | #define KMSG_COMPONENT "cio" |
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/errno.h> | 17 | #include <linux/errno.h> |
18 | #include <linux/list.h> | 18 | #include <linux/list.h> |
19 | #include <linux/reboot.h> | 19 | #include <linux/reboot.h> |
20 | #include <linux/suspend.h> | ||
20 | #include <asm/isc.h> | 21 | #include <asm/isc.h> |
21 | #include <asm/crw.h> | 22 | #include <asm/crw.h> |
22 | 23 | ||
@@ -780,6 +781,79 @@ static struct notifier_block css_reboot_notifier = { | |||
780 | }; | 781 | }; |
781 | 782 | ||
782 | /* | 783 | /* |
784 | * Since the css devices are neither on a bus nor have a class | ||
785 | * nor have a special device type, we cannot stop/restart channel | ||
786 | * path measurements via the normal suspend/resume callbacks, but have | ||
787 | * to use notifiers. | ||
788 | */ | ||
789 | static int css_power_event(struct notifier_block *this, unsigned long event, | ||
790 | void *ptr) | ||
791 | { | ||
792 | void *secm_area; | ||
793 | int ret, i; | ||
794 | |||
795 | switch (event) { | ||
796 | case PM_HIBERNATION_PREPARE: | ||
797 | case PM_SUSPEND_PREPARE: | ||
798 | ret = NOTIFY_DONE; | ||
799 | for (i = 0; i <= __MAX_CSSID; i++) { | ||
800 | struct channel_subsystem *css; | ||
801 | |||
802 | css = channel_subsystems[i]; | ||
803 | mutex_lock(&css->mutex); | ||
804 | if (!css->cm_enabled) { | ||
805 | mutex_unlock(&css->mutex); | ||
806 | continue; | ||
807 | } | ||
808 | secm_area = (void *)get_zeroed_page(GFP_KERNEL | | ||
809 | GFP_DMA); | ||
810 | if (secm_area) { | ||
811 | if (__chsc_do_secm(css, 0, secm_area)) | ||
812 | ret = NOTIFY_BAD; | ||
813 | free_page((unsigned long)secm_area); | ||
814 | } else | ||
815 | ret = NOTIFY_BAD; | ||
816 | |||
817 | mutex_unlock(&css->mutex); | ||
818 | } | ||
819 | break; | ||
820 | case PM_POST_HIBERNATION: | ||
821 | case PM_POST_SUSPEND: | ||
822 | ret = NOTIFY_DONE; | ||
823 | for (i = 0; i <= __MAX_CSSID; i++) { | ||
824 | struct channel_subsystem *css; | ||
825 | |||
826 | css = channel_subsystems[i]; | ||
827 | mutex_lock(&css->mutex); | ||
828 | if (!css->cm_enabled) { | ||
829 | mutex_unlock(&css->mutex); | ||
830 | continue; | ||
831 | } | ||
832 | secm_area = (void *)get_zeroed_page(GFP_KERNEL | | ||
833 | GFP_DMA); | ||
834 | if (secm_area) { | ||
835 | if (__chsc_do_secm(css, 1, secm_area)) | ||
836 | ret = NOTIFY_BAD; | ||
837 | free_page((unsigned long)secm_area); | ||
838 | } else | ||
839 | ret = NOTIFY_BAD; | ||
840 | |||
841 | mutex_unlock(&css->mutex); | ||
842 | } | ||
843 | /* search for subchannels, which appeared during hibernation */ | ||
844 | css_schedule_reprobe(); | ||
845 | break; | ||
846 | default: | ||
847 | ret = NOTIFY_DONE; | ||
848 | } | ||
849 | return ret; | ||
850 | |||
851 | } | ||
852 | static struct notifier_block css_power_notifier = { | ||
853 | .notifier_call = css_power_event, | ||
854 | }; | ||
855 | |||
856 | /* | ||
783 | * Now that the driver core is running, we can setup our channel subsystem. | 857 | * Now that the driver core is running, we can setup our channel subsystem. |
784 | * The struct subchannel's are created during probing (except for the | 858 | * The struct subchannel's are created during probing (except for the |
785 | * static console subchannel). | 859 | * static console subchannel). |
@@ -852,6 +926,11 @@ init_channel_subsystem (void) | |||
852 | ret = register_reboot_notifier(&css_reboot_notifier); | 926 | ret = register_reboot_notifier(&css_reboot_notifier); |
853 | if (ret) | 927 | if (ret) |
854 | goto out_unregister; | 928 | goto out_unregister; |
929 | ret = register_pm_notifier(&css_power_notifier); | ||
930 | if (ret) { | ||
931 | unregister_reboot_notifier(&css_reboot_notifier); | ||
932 | goto out_unregister; | ||
933 | } | ||
855 | css_init_done = 1; | 934 | css_init_done = 1; |
856 | 935 | ||
857 | /* Enable default isc for I/O subchannels. */ | 936 | /* Enable default isc for I/O subchannels. */ |
@@ -953,6 +1032,73 @@ static int css_uevent(struct device *dev, struct kobj_uevent_env *env) | |||
953 | return ret; | 1032 | return ret; |
954 | } | 1033 | } |
955 | 1034 | ||
1035 | static int css_pm_prepare(struct device *dev) | ||
1036 | { | ||
1037 | struct subchannel *sch = to_subchannel(dev); | ||
1038 | struct css_driver *drv; | ||
1039 | |||
1040 | if (mutex_is_locked(&sch->reg_mutex)) | ||
1041 | return -EAGAIN; | ||
1042 | if (!sch->dev.driver) | ||
1043 | return 0; | ||
1044 | drv = to_cssdriver(sch->dev.driver); | ||
1045 | /* Notify drivers that they may not register children. */ | ||
1046 | return drv->prepare ? drv->prepare(sch) : 0; | ||
1047 | } | ||
1048 | |||
1049 | static void css_pm_complete(struct device *dev) | ||
1050 | { | ||
1051 | struct subchannel *sch = to_subchannel(dev); | ||
1052 | struct css_driver *drv; | ||
1053 | |||
1054 | if (!sch->dev.driver) | ||
1055 | return; | ||
1056 | drv = to_cssdriver(sch->dev.driver); | ||
1057 | if (drv->complete) | ||
1058 | drv->complete(sch); | ||
1059 | } | ||
1060 | |||
1061 | static int css_pm_freeze(struct device *dev) | ||
1062 | { | ||
1063 | struct subchannel *sch = to_subchannel(dev); | ||
1064 | struct css_driver *drv; | ||
1065 | |||
1066 | if (!sch->dev.driver) | ||
1067 | return 0; | ||
1068 | drv = to_cssdriver(sch->dev.driver); | ||
1069 | return drv->freeze ? drv->freeze(sch) : 0; | ||
1070 | } | ||
1071 | |||
1072 | static int css_pm_thaw(struct device *dev) | ||
1073 | { | ||
1074 | struct subchannel *sch = to_subchannel(dev); | ||
1075 | struct css_driver *drv; | ||
1076 | |||
1077 | if (!sch->dev.driver) | ||
1078 | return 0; | ||
1079 | drv = to_cssdriver(sch->dev.driver); | ||
1080 | return drv->thaw ? drv->thaw(sch) : 0; | ||
1081 | } | ||
1082 | |||
1083 | static int css_pm_restore(struct device *dev) | ||
1084 | { | ||
1085 | struct subchannel *sch = to_subchannel(dev); | ||
1086 | struct css_driver *drv; | ||
1087 | |||
1088 | if (!sch->dev.driver) | ||
1089 | return 0; | ||
1090 | drv = to_cssdriver(sch->dev.driver); | ||
1091 | return drv->restore ? drv->restore(sch) : 0; | ||
1092 | } | ||
1093 | |||
1094 | static struct dev_pm_ops css_pm_ops = { | ||
1095 | .prepare = css_pm_prepare, | ||
1096 | .complete = css_pm_complete, | ||
1097 | .freeze = css_pm_freeze, | ||
1098 | .thaw = css_pm_thaw, | ||
1099 | .restore = css_pm_restore, | ||
1100 | }; | ||
1101 | |||
956 | struct bus_type css_bus_type = { | 1102 | struct bus_type css_bus_type = { |
957 | .name = "css", | 1103 | .name = "css", |
958 | .match = css_bus_match, | 1104 | .match = css_bus_match, |
@@ -960,6 +1106,7 @@ struct bus_type css_bus_type = { | |||
960 | .remove = css_remove, | 1106 | .remove = css_remove, |
961 | .shutdown = css_shutdown, | 1107 | .shutdown = css_shutdown, |
962 | .uevent = css_uevent, | 1108 | .uevent = css_uevent, |
1109 | .pm = &css_pm_ops, | ||
963 | }; | 1110 | }; |
964 | 1111 | ||
965 | /** | 1112 | /** |
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 57ebf120f825..9763eeec7458 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h | |||
@@ -70,6 +70,11 @@ struct chp_link; | |||
70 | * @probe: function called on probe | 70 | * @probe: function called on probe |
71 | * @remove: function called on remove | 71 | * @remove: function called on remove |
72 | * @shutdown: called at device shutdown | 72 | * @shutdown: called at device shutdown |
73 | * @prepare: prepare for pm state transition | ||
74 | * @complete: undo work done in @prepare | ||
75 | * @freeze: callback for freezing during hibernation snapshotting | ||
76 | * @thaw: undo work done in @freeze | ||
77 | * @restore: callback for restoring after hibernation | ||
73 | * @name: name of the device driver | 78 | * @name: name of the device driver |
74 | */ | 79 | */ |
75 | struct css_driver { | 80 | struct css_driver { |
@@ -82,6 +87,11 @@ struct css_driver { | |||
82 | int (*probe)(struct subchannel *); | 87 | int (*probe)(struct subchannel *); |
83 | int (*remove)(struct subchannel *); | 88 | int (*remove)(struct subchannel *); |
84 | void (*shutdown)(struct subchannel *); | 89 | void (*shutdown)(struct subchannel *); |
90 | int (*prepare) (struct subchannel *); | ||
91 | void (*complete) (struct subchannel *); | ||
92 | int (*freeze)(struct subchannel *); | ||
93 | int (*thaw) (struct subchannel *); | ||
94 | int (*restore)(struct subchannel *); | ||
85 | const char *name; | 95 | const char *name; |
86 | }; | 96 | }; |
87 | 97 | ||