aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390
diff options
context:
space:
mode:
authorSebastian Ott <sebott@linux.vnet.ibm.com>2009-06-16 04:30:22 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2009-06-16 04:31:09 -0400
commitdcbd16d5111258df7c821ec1e4124fe6ffbf3c16 (patch)
treeee095cc2a4fc2a81bde256f8125b0199fe02a9ff /drivers/s390
parent7e597a21a1470b12428cb0edd03c40986026451f (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.c3
-rw-r--r--drivers/s390/cio/chsc.h1
-rw-r--r--drivers/s390/cio/css.c157
-rw-r--r--drivers/s390/cio/css.h10
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
552static int 552int __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);
90extern int chsc_enable_facility(int); 90extern int chsc_enable_facility(int);
91struct channel_subsystem; 91struct channel_subsystem;
92extern int chsc_secm(struct channel_subsystem *, int); 92extern int chsc_secm(struct channel_subsystem *, int);
93int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page);
93 94
94int chsc_chp_vary(struct chp_id chpid, int on); 95int chsc_chp_vary(struct chp_id chpid, int on);
95int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, 96int 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 */
789static 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}
852static 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
1035static 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
1049static 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
1061static 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
1072static 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
1083static 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
1094static 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
956struct bus_type css_bus_type = { 1102struct 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 */
75struct css_driver { 80struct 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