mirror of
				https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
				synced 2025-10-31 12:09:34 +00:00 
			
		
		
		
	 dcb84f335b
			
		
	
	
		dcb84f335b
		
	
	
	
	
		
			
			cpuidle and acpi driver interaction bug with the way cpuidle_register_driver() is called. Due to this bug, there will be oops on AC<->DC on some systems, where they support C-states in one DC and not in AC. The current code does ON BOOT: Look at CST and other C-state info to see whether more than C1 is supported. If it is, then acpi processor_idle does a cpuidle_register_driver() call, which internally enables the device. ON CST change notification (AC<->DC) and on suspend-resume: acpi driver temporarily disables device, updates the device with any new C-states, and reenables the device. The problem is is on boot, there are no C2, C3 states supported and we skip the register. Later on AC<->DC, we may get a CST notification and we try to reevaluate CST and enabled the device, without actually registering it. This causes breakage as we try to create /sys fs sub directory, without the parent directory which is created at register time. Thanks to Sanjeev for reporting the problem here. http://bugzilla.kernel.org/show_bug.cgi?id=10394 Signed-off-by: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> Signed-off-by: Len Brown <len.brown@intel.com>
		
			
				
	
	
		
			192 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			192 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * cpuidle.h - a generic framework for CPU idle power management
 | |
|  *
 | |
|  * (C) 2007 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
 | |
|  *          Shaohua Li <shaohua.li@intel.com>
 | |
|  *          Adam Belay <abelay@novell.com>
 | |
|  *
 | |
|  * This code is licenced under the GPL.
 | |
|  */
 | |
| 
 | |
| #ifndef _LINUX_CPUIDLE_H
 | |
| #define _LINUX_CPUIDLE_H
 | |
| 
 | |
| #include <linux/percpu.h>
 | |
| #include <linux/list.h>
 | |
| #include <linux/module.h>
 | |
| #include <linux/kobject.h>
 | |
| #include <linux/completion.h>
 | |
| 
 | |
| #define CPUIDLE_STATE_MAX	8
 | |
| #define CPUIDLE_NAME_LEN	16
 | |
| #define CPUIDLE_DESC_LEN	32
 | |
| 
 | |
| struct cpuidle_device;
 | |
| 
 | |
| 
 | |
| /****************************
 | |
|  * CPUIDLE DEVICE INTERFACE *
 | |
|  ****************************/
 | |
| 
 | |
| struct cpuidle_state {
 | |
| 	char		name[CPUIDLE_NAME_LEN];
 | |
| 	char		desc[CPUIDLE_DESC_LEN];
 | |
| 	void		*driver_data;
 | |
| 
 | |
| 	unsigned int	flags;
 | |
| 	unsigned int	exit_latency; /* in US */
 | |
| 	unsigned int	power_usage; /* in mW */
 | |
| 	unsigned int	target_residency; /* in US */
 | |
| 
 | |
| 	unsigned long long	usage;
 | |
| 	unsigned long long	time; /* in US */
 | |
| 
 | |
| 	int (*enter)	(struct cpuidle_device *dev,
 | |
| 			 struct cpuidle_state *state);
 | |
| };
 | |
| 
 | |
| /* Idle State Flags */
 | |
| #define CPUIDLE_FLAG_TIME_VALID	(0x01) /* is residency time measurable? */
 | |
| #define CPUIDLE_FLAG_CHECK_BM	(0x02) /* BM activity will exit state */
 | |
| #define CPUIDLE_FLAG_POLL	(0x10) /* no latency, no savings */
 | |
| #define CPUIDLE_FLAG_SHALLOW	(0x20) /* low latency, minimal savings */
 | |
| #define CPUIDLE_FLAG_BALANCED	(0x40) /* medium latency, moderate savings */
 | |
| #define CPUIDLE_FLAG_DEEP	(0x80) /* high latency, large savings */
 | |
| 
 | |
| #define CPUIDLE_DRIVER_FLAGS_MASK (0xFFFF0000)
 | |
| 
 | |
| /**
 | |
|  * cpuidle_get_statedata - retrieves private driver state data
 | |
|  * @state: the state
 | |
|  */
 | |
| static inline void * cpuidle_get_statedata(struct cpuidle_state *state)
 | |
| {
 | |
| 	return state->driver_data;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * cpuidle_set_statedata - stores private driver state data
 | |
|  * @state: the state
 | |
|  * @data: the private data
 | |
|  */
 | |
| static inline void
 | |
| cpuidle_set_statedata(struct cpuidle_state *state, void *data)
 | |
| {
 | |
| 	state->driver_data = data;
 | |
| }
 | |
| 
 | |
| struct cpuidle_state_kobj {
 | |
| 	struct cpuidle_state *state;
 | |
| 	struct completion kobj_unregister;
 | |
| 	struct kobject kobj;
 | |
| };
 | |
| 
 | |
| struct cpuidle_device {
 | |
| 	unsigned int		registered:1;
 | |
| 	unsigned int		enabled:1;
 | |
| 	unsigned int		cpu;
 | |
| 
 | |
| 	int			last_residency;
 | |
| 	int			state_count;
 | |
| 	struct cpuidle_state	states[CPUIDLE_STATE_MAX];
 | |
| 	struct cpuidle_state_kobj *kobjs[CPUIDLE_STATE_MAX];
 | |
| 	struct cpuidle_state	*last_state;
 | |
| 
 | |
| 	struct list_head 	device_list;
 | |
| 	struct kobject		kobj;
 | |
| 	struct completion	kobj_unregister;
 | |
| 	void			*governor_data;
 | |
| 	struct cpuidle_state	*safe_state;
 | |
| };
 | |
| 
 | |
| DECLARE_PER_CPU(struct cpuidle_device *, cpuidle_devices);
 | |
| 
 | |
| /**
 | |
|  * cpuidle_get_last_residency - retrieves the last state's residency time
 | |
|  * @dev: the target CPU
 | |
|  *
 | |
|  * NOTE: this value is invalid if CPUIDLE_FLAG_TIME_VALID isn't set
 | |
|  */
 | |
| static inline int cpuidle_get_last_residency(struct cpuidle_device *dev)
 | |
| {
 | |
| 	return dev->last_residency;
 | |
| }
 | |
| 
 | |
| 
 | |
| /****************************
 | |
|  * CPUIDLE DRIVER INTERFACE *
 | |
|  ****************************/
 | |
| 
 | |
| struct cpuidle_driver {
 | |
| 	char			name[CPUIDLE_NAME_LEN];
 | |
| 	struct module 		*owner;
 | |
| };
 | |
| 
 | |
| #ifdef CONFIG_CPU_IDLE
 | |
| 
 | |
| extern int cpuidle_register_driver(struct cpuidle_driver *drv);
 | |
| extern void cpuidle_unregister_driver(struct cpuidle_driver *drv);
 | |
| extern int cpuidle_register_device(struct cpuidle_device *dev);
 | |
| extern void cpuidle_unregister_device(struct cpuidle_device *dev);
 | |
| 
 | |
| extern void cpuidle_pause_and_lock(void);
 | |
| extern void cpuidle_resume_and_unlock(void);
 | |
| extern int cpuidle_enable_device(struct cpuidle_device *dev);
 | |
| extern void cpuidle_disable_device(struct cpuidle_device *dev);
 | |
| 
 | |
| #else
 | |
| 
 | |
| static inline int cpuidle_register_driver(struct cpuidle_driver *drv)
 | |
| {return 0;}
 | |
| static inline void cpuidle_unregister_driver(struct cpuidle_driver *drv) { }
 | |
| static inline int cpuidle_register_device(struct cpuidle_device *dev)
 | |
| {return 0;}
 | |
| static inline void cpuidle_unregister_device(struct cpuidle_device *dev) { }
 | |
| 
 | |
| static inline void cpuidle_pause_and_lock(void) { }
 | |
| static inline void cpuidle_resume_and_unlock(void) { }
 | |
| static inline int cpuidle_enable_device(struct cpuidle_device *dev)
 | |
| {return 0;}
 | |
| static inline void cpuidle_disable_device(struct cpuidle_device *dev) { }
 | |
| 
 | |
| #endif
 | |
| 
 | |
| /******************************
 | |
|  * CPUIDLE GOVERNOR INTERFACE *
 | |
|  ******************************/
 | |
| 
 | |
| struct cpuidle_governor {
 | |
| 	char			name[CPUIDLE_NAME_LEN];
 | |
| 	struct list_head 	governor_list;
 | |
| 	unsigned int		rating;
 | |
| 
 | |
| 	int  (*enable)		(struct cpuidle_device *dev);
 | |
| 	void (*disable)		(struct cpuidle_device *dev);
 | |
| 
 | |
| 	int  (*select)		(struct cpuidle_device *dev);
 | |
| 	void (*reflect)		(struct cpuidle_device *dev);
 | |
| 
 | |
| 	struct module 		*owner;
 | |
| };
 | |
| 
 | |
| #ifdef CONFIG_CPU_IDLE
 | |
| 
 | |
| extern int cpuidle_register_governor(struct cpuidle_governor *gov);
 | |
| extern void cpuidle_unregister_governor(struct cpuidle_governor *gov);
 | |
| 
 | |
| #else
 | |
| 
 | |
| static inline int cpuidle_register_governor(struct cpuidle_governor *gov)
 | |
| {return 0;}
 | |
| static inline void cpuidle_unregister_governor(struct cpuidle_governor *gov) { }
 | |
| 
 | |
| #endif
 | |
| 
 | |
| #ifdef CONFIG_ARCH_HAS_CPU_RELAX
 | |
| #define CPUIDLE_DRIVER_STATE_START	1
 | |
| #else
 | |
| #define CPUIDLE_DRIVER_STATE_START	0
 | |
| #endif
 | |
| 
 | |
| #endif /* _LINUX_CPUIDLE_H */
 |