Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0ac85241 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman
Browse files

[PATCH] driver model wakeup flags



This is a refresh of an earlier patch to add "wakeup" support to the
PM core model.  This provides per-device bus-neutral control of the
use of wakeup events.

  * "struct device_pm_info" has two bits that are initialized as
    part of setting up the enclosing struct device:
      - "can_wakeup", reflecting hardware capabilities
      - "may_wakeup", the policy setting (when CONFIG_PM)

  * There's a writeable sysfs "wakeup" file, with one of two values:
      - "enabled", when the policy is to allow wakeup
      - "disabled", when the policy is not to allow it
      - "" if the device can't currently issue wakeups

By default, wakeup is enabled on all devices that support it.  If its
driver doesn't support it ... treat it as a bug.  :)

Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 2a7ff1fe
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -225,6 +225,7 @@ void device_initialize(struct device *dev)
		   klist_children_put);
		   klist_children_put);
	INIT_LIST_HEAD(&dev->dma_pools);
	INIT_LIST_HEAD(&dev->dma_pools);
	init_MUTEX(&dev->sem);
	init_MUTEX(&dev->sem);
	device_init_wakeup(dev, 0);
}
}


/**
/**
+73 −0
Original line number Original line Diff line number Diff line
@@ -48,8 +48,81 @@ static ssize_t state_store(struct device * dev, struct device_attribute *attr, c
static DEVICE_ATTR(state, 0644, state_show, state_store);
static DEVICE_ATTR(state, 0644, state_show, state_store);




/*
 *	wakeup - Report/change current wakeup option for device
 *
 *	Some devices support "wakeup" events, which are hardware signals
 *	used to activate devices from suspended or low power states.  Such
 *	devices have one of three values for the sysfs power/wakeup file:
 *
 *	 + "enabled\n" to issue the events;
 *	 + "disabled\n" not to do so; or
 *	 + "\n" for temporary or permanent inability to issue wakeup.
 *
 *	(For example, unconfigured USB devices can't issue wakeups.)
 *
 *	Familiar examples of devices that can issue wakeup events include
 *	keyboards and mice (both PS2 and USB styles), power buttons, modems,
 *	"Wake-On-LAN" Ethernet links, GPIO lines, and more.  Some events
 *	will wake the entire system from a suspend state; others may just
 *	wake up the device (if the system as a whole is already active).
 *	Some wakeup events use normal IRQ lines; other use special out
 *	of band signaling.
 *
 *	It is the responsibility of device drivers to enable (or disable)
 *	wakeup signaling as part of changing device power states, respecting
 *	the policy choices provided through the driver model.
 *
 *	Devices may not be able to generate wakeup events from all power
 *	states.  Also, the events may be ignored in some configurations;
 *	for example, they might need help from other devices that aren't
 *	active, or which may have wakeup disabled.  Some drivers rely on
 *	wakeup events internally (unless they are disabled), keeping
 *	their hardware in low power modes whenever they're unused.  This
 *	saves runtime power, without requiring system-wide sleep states.
 */

static const char enabled[] = "enabled";
static const char disabled[] = "disabled";

static ssize_t
wake_show(struct device * dev, struct device_attribute *attr, char * buf)
{
	return sprintf(buf, "%s\n", device_can_wakeup(dev)
		? (device_may_wakeup(dev) ? enabled : disabled)
		: "");
}

static ssize_t
wake_store(struct device * dev, struct device_attribute *attr,
	const char * buf, size_t n)
{
	char *cp;
	int len = n;

	if (!device_can_wakeup(dev))
		return -EINVAL;

	cp = memchr(buf, '\n', n);
	if (cp)
		len = cp - buf;
	if (len == sizeof enabled - 1
			&& strncmp(buf, enabled, sizeof enabled - 1) == 0)
		device_set_wakeup_enable(dev, 1);
	else if (len == sizeof disabled - 1
			&& strncmp(buf, disabled, sizeof disabled - 1) == 0)
		device_set_wakeup_enable(dev, 0);
	else
		return -EINVAL;
	return n;
}

static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store);


static struct attribute * power_attrs[] = {
static struct attribute * power_attrs[] = {
	&dev_attr_state.attr,
	&dev_attr_state.attr,
	&dev_attr_wakeup.attr,
	NULL,
	NULL,
};
};
static struct attribute_group pm_attr_group = {
static struct attribute_group pm_attr_group = {
+25 −1
Original line number Original line Diff line number Diff line
@@ -219,7 +219,9 @@ typedef struct pm_message {


struct dev_pm_info {
struct dev_pm_info {
	pm_message_t		power_state;
	pm_message_t		power_state;
	unsigned		can_wakeup:1;
#ifdef	CONFIG_PM
#ifdef	CONFIG_PM
	unsigned		should_wakeup:1;
	pm_message_t		prev_state;
	pm_message_t		prev_state;
	void			* saved_state;
	void			* saved_state;
	atomic_t		pm_users;
	atomic_t		pm_users;
@@ -236,13 +238,35 @@ extern void device_resume(void);


#ifdef CONFIG_PM
#ifdef CONFIG_PM
extern int device_suspend(pm_message_t state);
extern int device_suspend(pm_message_t state);
#else

#define device_set_wakeup_enable(dev,val) \
	((dev)->power.should_wakeup = !!(val))
#define device_may_wakeup(dev) \
	(device_can_wakeup(dev) && (dev)->power.should_wakeup)

#else /* !CONFIG_PM */

static inline int device_suspend(pm_message_t state)
static inline int device_suspend(pm_message_t state)
{
{
	return 0;
	return 0;
}
}

#define device_set_wakeup_enable(dev,val)	do{}while(0)
#define device_may_wakeup(dev)			(0)

#endif
#endif


/* changes to device_may_wakeup take effect on the next pm state change.
 * by default, devices should wakeup if they can.
 */
#define device_can_wakeup(dev) \
	((dev)->power.can_wakeup)
#define device_init_wakeup(dev,val) \
	do { \
		device_can_wakeup(dev) = !!(val); \
		device_set_wakeup_enable(dev,val); \
	} while(0)

#endif /* __KERNEL__ */
#endif /* __KERNEL__ */


#endif /* _LINUX_PM_H */
#endif /* _LINUX_PM_H */