Commit dcbd16d5 authored by Sebastian Ott's avatar Sebastian Ott Committed by Martin Schwidefsky

[S390] pm: css bus power management callbacks

Signed-off-by: default avatarSebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 7e597a21
...@@ -549,8 +549,7 @@ chsc_add_cmg_attr(struct channel_subsystem *css) ...@@ -549,8 +549,7 @@ chsc_add_cmg_attr(struct channel_subsystem *css)
return ret; return ret;
} }
static int int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page)
__chsc_do_secm(struct channel_subsystem *css, int enable, void *page)
{ {
struct { struct {
struct chsc_header request; struct chsc_header request;
......
...@@ -90,6 +90,7 @@ extern void chsc_free_sei_area(void); ...@@ -90,6 +90,7 @@ extern void chsc_free_sei_area(void);
extern int chsc_enable_facility(int); extern int chsc_enable_facility(int);
struct channel_subsystem; struct channel_subsystem;
extern int chsc_secm(struct channel_subsystem *, int); extern int chsc_secm(struct channel_subsystem *, int);
int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page);
int chsc_chp_vary(struct chp_id chpid, int on); int chsc_chp_vary(struct chp_id chpid, int on);
int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
......
/* /*
* drivers/s390/cio/css.c * driver for channel subsystem
* driver for channel subsystem
* *
* Copyright IBM Corp. 2002,2008 * Copyright IBM Corp. 2002, 2009
* Author(s): Arnd Bergmann (arndb@de.ibm.com) *
* Cornelia Huck (cornelia.huck@de.ibm.com) * Author(s): Arnd Bergmann (arndb@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com)
*/ */
#define KMSG_COMPONENT "cio" #define KMSG_COMPONENT "cio"
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/suspend.h>
#include <asm/isc.h> #include <asm/isc.h>
#include <asm/crw.h> #include <asm/crw.h>
...@@ -779,6 +780,79 @@ static struct notifier_block css_reboot_notifier = { ...@@ -779,6 +780,79 @@ static struct notifier_block css_reboot_notifier = {
.notifier_call = css_reboot_event, .notifier_call = css_reboot_event,
}; };
/*
* Since the css devices are neither on a bus nor have a class
* nor have a special device type, we cannot stop/restart channel
* path measurements via the normal suspend/resume callbacks, but have
* to use notifiers.
*/
static int css_power_event(struct notifier_block *this, unsigned long event,
void *ptr)
{
void *secm_area;
int ret, i;
switch (event) {
case PM_HIBERNATION_PREPARE:
case PM_SUSPEND_PREPARE:
ret = NOTIFY_DONE;
for (i = 0; i <= __MAX_CSSID; i++) {
struct channel_subsystem *css;
css = channel_subsystems[i];
mutex_lock(&css->mutex);
if (!css->cm_enabled) {
mutex_unlock(&css->mutex);
continue;
}
secm_area = (void *)get_zeroed_page(GFP_KERNEL |
GFP_DMA);
if (secm_area) {
if (__chsc_do_secm(css, 0, secm_area))
ret = NOTIFY_BAD;
free_page((unsigned long)secm_area);
} else
ret = NOTIFY_BAD;
mutex_unlock(&css->mutex);
}
break;
case PM_POST_HIBERNATION:
case PM_POST_SUSPEND:
ret = NOTIFY_DONE;
for (i = 0; i <= __MAX_CSSID; i++) {
struct channel_subsystem *css;
css = channel_subsystems[i];
mutex_lock(&css->mutex);
if (!css->cm_enabled) {
mutex_unlock(&css->mutex);
continue;
}
secm_area = (void *)get_zeroed_page(GFP_KERNEL |
GFP_DMA);
if (secm_area) {
if (__chsc_do_secm(css, 1, secm_area))
ret = NOTIFY_BAD;
free_page((unsigned long)secm_area);
} else
ret = NOTIFY_BAD;
mutex_unlock(&css->mutex);
}
/* search for subchannels, which appeared during hibernation */
css_schedule_reprobe();
break;
default:
ret = NOTIFY_DONE;
}
return ret;
}
static struct notifier_block css_power_notifier = {
.notifier_call = css_power_event,
};
/* /*
* Now that the driver core is running, we can setup our channel subsystem. * Now that the driver core is running, we can setup our channel subsystem.
* The struct subchannel's are created during probing (except for the * The struct subchannel's are created during probing (except for the
...@@ -852,6 +926,11 @@ init_channel_subsystem (void) ...@@ -852,6 +926,11 @@ init_channel_subsystem (void)
ret = register_reboot_notifier(&css_reboot_notifier); ret = register_reboot_notifier(&css_reboot_notifier);
if (ret) if (ret)
goto out_unregister; goto out_unregister;
ret = register_pm_notifier(&css_power_notifier);
if (ret) {
unregister_reboot_notifier(&css_reboot_notifier);
goto out_unregister;
}
css_init_done = 1; css_init_done = 1;
/* Enable default isc for I/O subchannels. */ /* Enable default isc for I/O subchannels. */
...@@ -953,6 +1032,73 @@ static int css_uevent(struct device *dev, struct kobj_uevent_env *env) ...@@ -953,6 +1032,73 @@ static int css_uevent(struct device *dev, struct kobj_uevent_env *env)
return ret; return ret;
} }
static int css_pm_prepare(struct device *dev)
{
struct subchannel *sch = to_subchannel(dev);
struct css_driver *drv;
if (mutex_is_locked(&sch->reg_mutex))
return -EAGAIN;
if (!sch->dev.driver)
return 0;
drv = to_cssdriver(sch->dev.driver);
/* Notify drivers that they may not register children. */
return drv->prepare ? drv->prepare(sch) : 0;
}
static void css_pm_complete(struct device *dev)
{
struct subchannel *sch = to_subchannel(dev);
struct css_driver *drv;
if (!sch->dev.driver)
return;
drv = to_cssdriver(sch->dev.driver);
if (drv->complete)
drv->complete(sch);
}
static int css_pm_freeze(struct device *dev)
{
struct subchannel *sch = to_subchannel(dev);
struct css_driver *drv;
if (!sch->dev.driver)
return 0;
drv = to_cssdriver(sch->dev.driver);
return drv->freeze ? drv->freeze(sch) : 0;
}
static int css_pm_thaw(struct device *dev)
{
struct subchannel *sch = to_subchannel(dev);
struct css_driver *drv;
if (!sch->dev.driver)
return 0;
drv = to_cssdriver(sch->dev.driver);
return drv->thaw ? drv->thaw(sch) : 0;
}
static int css_pm_restore(struct device *dev)
{
struct subchannel *sch = to_subchannel(dev);
struct css_driver *drv;
if (!sch->dev.driver)
return 0;
drv = to_cssdriver(sch->dev.driver);
return drv->restore ? drv->restore(sch) : 0;
}
static struct dev_pm_ops css_pm_ops = {
.prepare = css_pm_prepare,
.complete = css_pm_complete,
.freeze = css_pm_freeze,
.thaw = css_pm_thaw,
.restore = css_pm_restore,
};
struct bus_type css_bus_type = { struct bus_type css_bus_type = {
.name = "css", .name = "css",
.match = css_bus_match, .match = css_bus_match,
...@@ -960,6 +1106,7 @@ struct bus_type css_bus_type = { ...@@ -960,6 +1106,7 @@ struct bus_type css_bus_type = {
.remove = css_remove, .remove = css_remove,
.shutdown = css_shutdown, .shutdown = css_shutdown,
.uevent = css_uevent, .uevent = css_uevent,
.pm = &css_pm_ops,
}; };
/** /**
......
...@@ -70,6 +70,11 @@ struct chp_link; ...@@ -70,6 +70,11 @@ struct chp_link;
* @probe: function called on probe * @probe: function called on probe
* @remove: function called on remove * @remove: function called on remove
* @shutdown: called at device shutdown * @shutdown: called at device shutdown
* @prepare: prepare for pm state transition
* @complete: undo work done in @prepare
* @freeze: callback for freezing during hibernation snapshotting
* @thaw: undo work done in @freeze
* @restore: callback for restoring after hibernation
* @name: name of the device driver * @name: name of the device driver
*/ */
struct css_driver { struct css_driver {
...@@ -82,6 +87,11 @@ struct css_driver { ...@@ -82,6 +87,11 @@ struct css_driver {
int (*probe)(struct subchannel *); int (*probe)(struct subchannel *);
int (*remove)(struct subchannel *); int (*remove)(struct subchannel *);
void (*shutdown)(struct subchannel *); void (*shutdown)(struct subchannel *);
int (*prepare) (struct subchannel *);
void (*complete) (struct subchannel *);
int (*freeze)(struct subchannel *);
int (*thaw) (struct subchannel *);
int (*restore)(struct subchannel *);
const char *name; const char *name;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment