From: Pavel Machek This introduces pm_message_t. For now, it is only good for type-safety and sparse checking, but plan is to turn pm_message_t into structure soon. Signed-off-by: Andrew Morton --- 25-akpm/Documentation/power/devices.txt | 88 ++++++++++++++++++++++++++++++++ 25-akpm/arch/arm/common/amba.c | 2 25-akpm/arch/arm/common/locomo.c | 2 25-akpm/arch/arm/common/sa1111.c | 2 25-akpm/drivers/base/platform.c | 2 25-akpm/drivers/base/power/power.h | 6 +- 25-akpm/drivers/base/power/runtime.c | 4 - 25-akpm/drivers/base/power/suspend.c | 8 +- 25-akpm/drivers/ide/ide.c | 2 25-akpm/drivers/pci/pci-driver.c | 2 25-akpm/include/linux/device.h | 2 25-akpm/include/linux/pm.h | 32 ++++++++++- 12 files changed, 132 insertions(+), 20 deletions(-) diff -puN arch/arm/common/amba.c~pm-introduce-pm_message_t arch/arm/common/amba.c --- 25/arch/arm/common/amba.c~pm-introduce-pm_message_t Tue Jan 4 15:57:20 2005 +++ 25-akpm/arch/arm/common/amba.c Tue Jan 4 15:57:20 2005 @@ -59,7 +59,7 @@ static int amba_hotplug(struct device *d #define amba_hotplug NULL #endif -static int amba_suspend(struct device *dev, u32 state) +static int amba_suspend(struct device *dev, pm_message_t state) { struct amba_driver *drv = to_amba_driver(dev->driver); int ret = 0; diff -puN arch/arm/common/locomo.c~pm-introduce-pm_message_t arch/arm/common/locomo.c --- 25/arch/arm/common/locomo.c~pm-introduce-pm_message_t Tue Jan 4 15:57:20 2005 +++ 25-akpm/arch/arm/common/locomo.c Tue Jan 4 15:57:20 2005 @@ -668,7 +668,7 @@ static int locomo_match(struct device *_ return dev->devid == drv->devid; } -static int locomo_bus_suspend(struct device *dev, u32 state) +static int locomo_bus_suspend(struct device *dev, pm_message_t state) { struct locomo_dev *ldev = LOCOMO_DEV(dev); struct locomo_driver *drv = LOCOMO_DRV(dev->driver); diff -puN arch/arm/common/sa1111.c~pm-introduce-pm_message_t arch/arm/common/sa1111.c --- 25/arch/arm/common/sa1111.c~pm-introduce-pm_message_t Tue Jan 4 15:57:20 2005 +++ 25-akpm/arch/arm/common/sa1111.c Tue Jan 4 15:57:20 2005 @@ -1194,7 +1194,7 @@ static int sa1111_match(struct device *_ return dev->devid == drv->devid; } -static int sa1111_bus_suspend(struct device *dev, u32 state) +static int sa1111_bus_suspend(struct device *dev, pm_message_t state) { struct sa1111_dev *sadev = SA1111_DEV(dev); struct sa1111_driver *drv = SA1111_DRV(dev->driver); diff -puN Documentation/power/devices.txt~pm-introduce-pm_message_t Documentation/power/devices.txt --- 25/Documentation/power/devices.txt~pm-introduce-pm_message_t Tue Jan 4 15:57:20 2005 +++ 25-akpm/Documentation/power/devices.txt Tue Jan 4 15:57:20 2005 @@ -118,6 +118,94 @@ will fail. There is currently no way to know what states a device or driver supports a priori. This will change in the future. +pm_message_t meaning + +pm_message_t has two fields. event ("major"), and flags. If driver +does not know event code, it aborts the request, returning error. Some +drivers may need to deal with special cases based on the actual type +of suspend operation being done at the system level. This is why +there are flags. + +Event codes are: + +ON -- no need to do anything except special cases like broken +HW. + +# NOTIFICATION -- pretty much same as ON? + +FREEZE -- stop DMA and interrupts, and be prepared to reinit HW from +scratch. That probably means stop accepting upstream requests, the +actual policy of what to do with them beeing specific to a given +driver. It's acceptable for a network driver to just drop packets +while a block driver is expected to block the queue so no request is +lost. (Use IDE as an example on how to do that). FREEZE requires no +power state change, and it's expected for drivers to be able to +quickly transition back to operating state. + +SUSPEND -- like FREEZE, but also put hardware into low-power state. If +there's need to distinguish several levels of sleep, additional flag +is probably best way to do that. + +Transitions are only from a resumed state to a suspended state, never +between 2 suspended states. (ON -> FREEZE or ON -> SUSPEND can happen, +FREEZE -> SUSPEND or SUSPEND -> FREEZE can not). + +All events are: + +[NOTE NOTE NOTE: If you are driver author, you should not care; you +should only look at event, and ignore flags.] + +#Prepare for suspend -- userland is still running but we are going to +#enter suspend state. This gives drivers chance to load firmware from +#disk and store it in memory, or do other activities taht require +#operating userland, ability to kmalloc GFP_KERNEL, etc... All of these +#are forbiden once the suspend dance is started.. event = ON, flags = +#PREPARE_TO_SUSPEND + +Apm standby -- prepare for APM event. Quiesce devices to make life +easier for APM BIOS. event = FREEZE, flags = APM_STANDBY + +Apm suspend -- same as APM_STANDBY, but it we should probably avoid +spinning down disks. event = FREEZE, flags = APM_SUSPEND + +System halt, reboot -- quiesce devices to make life easier for BIOS. event += FREEZE, flags = SYSTEM_HALT or SYSTEM_REBOOT + +System shutdown -- at least disks need to be spun down, or data may be +lost. Quiesce devices, just to make life easier for BIOS. event = +FREEZE, flags = SYSTEM_SHUTDOWN + +Kexec -- turn off DMAs and put hardware into some state where new +kernel can take over. event = FREEZE, flags = KEXEC + +Powerdown at end of swsusp -- very similar to SYSTEM_SHUTDOWN, except wake +may need to be enabled on some devices. This actually has at least 3 +subtypes, system can reboot, enter S4 and enter S5 at the end of +swsusp. event = FREEZE, flags = SWSUSP and one of SYSTEM_REBOOT, +SYSTEM_SHUTDOWN, SYSTEM_S4 + +Suspend to ram -- put devices into low power state. event = SUSPEND, +flags = SUSPEND_TO_RAM + +Freeze for swsusp snapshot -- stop DMA and interrupts. No need to put +devices into low power mode, but you must be able to reinitialize +device from scratch in resume method. This has two flavors, its done +once on suspending kernel, once on resuming kernel. event = FREEZE, +flags = DURING_SUSPEND or DURING_RESUME + +Device detach requested from /sys -- deinitialize device; proably same as +SYSTEM_SHUTDOWN, I do not understand this one too much. probably event += FREEZE, flags = DEV_DETACH. + +#These are not really events sent: +# +#System fully on -- device is working normally; this is probably never +#passed to suspend() method... event = ON, flags = 0 +# +#Ready after resume -- userland is now running, again. Time to free any +#memory you ate during prepare to suspend... event = ON, flags = +#READY_AFTER_RESUME +# Driver Detach Power Management diff -puN drivers/base/platform.c~pm-introduce-pm_message_t drivers/base/platform.c --- 25/drivers/base/platform.c~pm-introduce-pm_message_t Tue Jan 4 15:57:20 2005 +++ 25-akpm/drivers/base/platform.c Tue Jan 4 15:57:20 2005 @@ -238,7 +238,7 @@ static int platform_match(struct device return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0); } -static int platform_suspend(struct device * dev, u32 state) +static int platform_suspend(struct device * dev, pm_message_t state) { int ret = 0; diff -puN drivers/base/power/power.h~pm-introduce-pm_message_t drivers/base/power/power.h --- 25/drivers/base/power/power.h~pm-introduce-pm_message_t Tue Jan 4 15:57:20 2005 +++ 25-akpm/drivers/base/power/power.h Tue Jan 4 15:57:20 2005 @@ -71,14 +71,14 @@ extern int resume_device(struct device * /* * suspend.c */ -extern int suspend_device(struct device *, u32); +extern int suspend_device(struct device *, pm_message_t); /* * runtime.c */ -extern int dpm_runtime_suspend(struct device *, u32); +extern int dpm_runtime_suspend(struct device *, pm_message_t); extern void dpm_runtime_resume(struct device *); #else /* CONFIG_PM */ @@ -93,7 +93,7 @@ static inline void device_pm_remove(stru } -static inline int dpm_runtime_suspend(struct device * dev, u32 state) +static inline int dpm_runtime_suspend(struct device * dev, pm_message_t state) { return 0; } diff -puN drivers/base/power/runtime.c~pm-introduce-pm_message_t drivers/base/power/runtime.c --- 25/drivers/base/power/runtime.c~pm-introduce-pm_message_t Tue Jan 4 15:57:20 2005 +++ 25-akpm/drivers/base/power/runtime.c Tue Jan 4 15:57:20 2005 @@ -44,7 +44,7 @@ void dpm_runtime_resume(struct device * * @state: State to enter. */ -int dpm_runtime_suspend(struct device * dev, u32 state) +int dpm_runtime_suspend(struct device * dev, pm_message_t state) { int error = 0; @@ -73,7 +73,7 @@ int dpm_runtime_suspend(struct device * * always be able to tell, but we need accurate information to * work reliably. */ -void dpm_set_power_state(struct device * dev, u32 state) +void dpm_set_power_state(struct device * dev, pm_message_t state) { down(&dpm_sem); dev->power.power_state = state; diff -puN drivers/base/power/suspend.c~pm-introduce-pm_message_t drivers/base/power/suspend.c --- 25/drivers/base/power/suspend.c~pm-introduce-pm_message_t Tue Jan 4 15:57:20 2005 +++ 25-akpm/drivers/base/power/suspend.c Tue Jan 4 15:57:20 2005 @@ -11,7 +11,7 @@ #include #include "power.h" -extern int sysdev_suspend(u32 state); +extern int sysdev_suspend(pm_message_t state); /* * The entries in the dpm_active list are in a depth first order, simply @@ -35,7 +35,7 @@ extern int sysdev_suspend(u32 state); * @state: Power state device is entering. */ -int suspend_device(struct device * dev, u32 state) +int suspend_device(struct device * dev, pm_message_t state) { int error = 0; @@ -65,7 +65,7 @@ int suspend_device(struct device * dev, * */ -int device_suspend(u32 state) +int device_suspend(pm_message_t state) { int error = 0; @@ -118,7 +118,7 @@ EXPORT_SYMBOL_GPL(device_suspend); * done, power down system devices. */ -int device_power_down(u32 state) +int device_power_down(pm_message_t state) { int error = 0; struct device * dev; diff -puN drivers/ide/ide.c~pm-introduce-pm_message_t drivers/ide/ide.c --- 25/drivers/ide/ide.c~pm-introduce-pm_message_t Tue Jan 4 15:57:20 2005 +++ 25-akpm/drivers/ide/ide.c Tue Jan 4 15:57:20 2005 @@ -1510,7 +1510,7 @@ int ata_attach(ide_drive_t *drive) return 1; } -static int generic_ide_suspend(struct device *dev, u32 state) +static int generic_ide_suspend(struct device *dev, pm_message_t state) { ide_drive_t *drive = dev->driver_data; struct request rq; diff -puN drivers/pci/pci-driver.c~pm-introduce-pm_message_t drivers/pci/pci-driver.c --- 25/drivers/pci/pci-driver.c~pm-introduce-pm_message_t Tue Jan 4 15:57:20 2005 +++ 25-akpm/drivers/pci/pci-driver.c Tue Jan 4 15:57:20 2005 @@ -284,7 +284,7 @@ static int pci_device_remove(struct devi return 0; } -static int pci_device_suspend(struct device * dev, u32 state) +static int pci_device_suspend(struct device * dev, pm_message_t state) { struct pci_dev * pci_dev = to_pci_dev(dev); struct pci_driver * drv = pci_dev->driver; diff -puN include/linux/device.h~pm-introduce-pm_message_t include/linux/device.h --- 25/include/linux/device.h~pm-introduce-pm_message_t Tue Jan 4 15:57:20 2005 +++ 25-akpm/include/linux/device.h Tue Jan 4 15:57:20 2005 @@ -61,7 +61,7 @@ struct bus_type { int (*match)(struct device * dev, struct device_driver * drv); int (*hotplug) (struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size); - int (*suspend)(struct device * dev, u32 state); + int (*suspend)(struct device * dev, pm_message_t state); int (*resume)(struct device * dev); }; diff -puN include/linux/pm.h~pm-introduce-pm_message_t include/linux/pm.h --- 25/include/linux/pm.h~pm-introduce-pm_message_t Tue Jan 4 15:57:20 2005 +++ 25-akpm/include/linux/pm.h Tue Jan 4 15:57:20 2005 @@ -222,10 +222,34 @@ extern int pm_suspend(suspend_state_t st struct device; +typedef u32 __bitwise pm_message_t; + +/* + * There are 4 important states driver can be in: + * ON -- driver is working + * FREEZE -- stop operations and apply whatever policy is applicable to a suspended driver + * of that class, freeze queues for block like IDE does, drop packets for + * ethernet, etc... stop DMA engine too etc... so a consistent image can be + * saved; but do not power any hardware down. + * SUSPEND - like FREEZE, but hardware is doing as much powersaving as possible. Roughly + * pci D3. + * + * Unfortunately, current drivers only recognize numeric values 0 (ON) and 3 (SUSPEND). + * We'll need to fix the drivers. So yes, putting 3 to all diferent defines is intentional, + * and will go away as soon as drivers are fixed. Also note that typedef is neccessary, + * we'll probably want to switch to + * typedef struct pm_message_t { int event; int flags; } pm_message_t + * or something similar soon. + */ + +#define PMSG_FREEZE ((__force pm_message_t) 3) +#define PMSG_SUSPEND ((__force pm_message_t) 3) +#define PMSG_ON ((__force pm_message_t) 0) + struct dev_pm_info { - u32 power_state; + pm_message_t power_state; #ifdef CONFIG_PM - u32 prev_state; + pm_message_t prev_state; void * saved_state; atomic_t pm_users; struct device * pm_parent; @@ -235,8 +259,8 @@ struct dev_pm_info { extern void device_pm_set_parent(struct device * dev, struct device * parent); -extern int device_suspend(u32 state); -extern int device_power_down(u32 state); +extern int device_suspend(pm_message_t state); +extern int device_power_down(pm_message_t state); extern void device_power_up(void); extern void device_resume(void); _