[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>
This commit is contained in:
Родитель
7e597a21a1
Коммит
dcbd16d511
|
@ -549,8 +549,7 @@ cleanup:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
__chsc_do_secm(struct channel_subsystem *css, int enable, void *page)
|
||||
int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page)
|
||||
{
|
||||
struct {
|
||||
struct chsc_header request;
|
||||
|
|
|
@ -90,6 +90,7 @@ extern void chsc_free_sei_area(void);
|
|||
extern int chsc_enable_facility(int);
|
||||
struct channel_subsystem;
|
||||
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_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
/*
|
||||
* drivers/s390/cio/css.c
|
||||
* driver for channel subsystem
|
||||
* driver for channel subsystem
|
||||
*
|
||||
* Copyright IBM Corp. 2002,2008
|
||||
* Author(s): Arnd Bergmann (arndb@de.ibm.com)
|
||||
* Cornelia Huck (cornelia.huck@de.ibm.com)
|
||||
* Copyright IBM Corp. 2002, 2009
|
||||
*
|
||||
* Author(s): Arnd Bergmann (arndb@de.ibm.com)
|
||||
* Cornelia Huck (cornelia.huck@de.ibm.com)
|
||||
*/
|
||||
|
||||
#define KMSG_COMPONENT "cio"
|
||||
|
@ -17,6 +17,7 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <asm/isc.h>
|
||||
#include <asm/crw.h>
|
||||
|
||||
|
@ -779,6 +780,79 @@ static struct notifier_block css_reboot_notifier = {
|
|||
.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.
|
||||
* The struct subchannel's are created during probing (except for the
|
||||
|
@ -852,6 +926,11 @@ init_channel_subsystem (void)
|
|||
ret = register_reboot_notifier(&css_reboot_notifier);
|
||||
if (ret)
|
||||
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;
|
||||
|
||||
/* Enable default isc for I/O subchannels. */
|
||||
|
@ -953,6 +1032,73 @@ static int css_uevent(struct device *dev, struct kobj_uevent_env *env)
|
|||
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 = {
|
||||
.name = "css",
|
||||
.match = css_bus_match,
|
||||
|
@ -960,6 +1106,7 @@ struct bus_type css_bus_type = {
|
|||
.remove = css_remove,
|
||||
.shutdown = css_shutdown,
|
||||
.uevent = css_uevent,
|
||||
.pm = &css_pm_ops,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -70,6 +70,11 @@ struct chp_link;
|
|||
* @probe: function called on probe
|
||||
* @remove: function called on remove
|
||||
* @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
|
||||
*/
|
||||
struct css_driver {
|
||||
|
@ -82,6 +87,11 @@ struct css_driver {
|
|||
int (*probe)(struct subchannel *);
|
||||
int (*remove)(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;
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче