xref: /openbmc/linux/drivers/cpuidle/sysfs.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
14f86d3a8SLen Brown /*
24f86d3a8SLen Brown  * sysfs.c - sysfs support
34f86d3a8SLen Brown  *
44f86d3a8SLen Brown  * (C) 2006-2007 Shaohua Li <shaohua.li@intel.com>
54f86d3a8SLen Brown  *
64f86d3a8SLen Brown  * This code is licenced under the GPL.
74f86d3a8SLen Brown  */
84f86d3a8SLen Brown 
94f86d3a8SLen Brown #include <linux/kernel.h>
104f86d3a8SLen Brown #include <linux/cpuidle.h>
114f86d3a8SLen Brown #include <linux/sysfs.h>
125a0e3ad6STejun Heo #include <linux/slab.h>
134f86d3a8SLen Brown #include <linux/cpu.h>
14728ce22bSDaniel Lezcano #include <linux/completion.h>
153a53396bSShuoX Liu #include <linux/capability.h>
168f3e9953SDaniel Lezcano #include <linux/device.h>
17728ce22bSDaniel Lezcano #include <linux/kobject.h>
184f86d3a8SLen Brown 
194f86d3a8SLen Brown #include "cpuidle.h"
204f86d3a8SLen Brown 
show_available_governors(struct device * dev,struct device_attribute * attr,char * buf)218a25a2fdSKay Sievers static ssize_t show_available_governors(struct device *dev,
228a25a2fdSKay Sievers 					struct device_attribute *attr,
2366198f36SRabin Vincent 					char *buf)
244f86d3a8SLen Brown {
254f86d3a8SLen Brown 	ssize_t i = 0;
264f86d3a8SLen Brown 	struct cpuidle_governor *tmp;
274f86d3a8SLen Brown 
284f86d3a8SLen Brown 	mutex_lock(&cpuidle_lock);
294f86d3a8SLen Brown 	list_for_each_entry(tmp, &cpuidle_governors, governor_list) {
303f9f8daaSHanjun Guo 		if (i >= (ssize_t) (PAGE_SIZE - (CPUIDLE_NAME_LEN + 2)))
314f86d3a8SLen Brown 			goto out;
323f9f8daaSHanjun Guo 
333f9f8daaSHanjun Guo 		i += scnprintf(&buf[i], CPUIDLE_NAME_LEN + 1, "%s ", tmp->name);
344f86d3a8SLen Brown 	}
354f86d3a8SLen Brown 
364f86d3a8SLen Brown out:
374f86d3a8SLen Brown 	i+= sprintf(&buf[i], "\n");
384f86d3a8SLen Brown 	mutex_unlock(&cpuidle_lock);
394f86d3a8SLen Brown 	return i;
404f86d3a8SLen Brown }
414f86d3a8SLen Brown 
show_current_driver(struct device * dev,struct device_attribute * attr,char * buf)428a25a2fdSKay Sievers static ssize_t show_current_driver(struct device *dev,
438a25a2fdSKay Sievers 				   struct device_attribute *attr,
4466198f36SRabin Vincent 				   char *buf)
454f86d3a8SLen Brown {
464f86d3a8SLen Brown 	ssize_t ret;
471f6b9f74SViresh Kumar 	struct cpuidle_driver *drv;
484f86d3a8SLen Brown 
494f86d3a8SLen Brown 	spin_lock(&cpuidle_driver_lock);
501f6b9f74SViresh Kumar 	drv = cpuidle_get_driver();
511f6b9f74SViresh Kumar 	if (drv)
521f6b9f74SViresh Kumar 		ret = sprintf(buf, "%s\n", drv->name);
534f86d3a8SLen Brown 	else
544f86d3a8SLen Brown 		ret = sprintf(buf, "none\n");
554f86d3a8SLen Brown 	spin_unlock(&cpuidle_driver_lock);
564f86d3a8SLen Brown 
574f86d3a8SLen Brown 	return ret;
584f86d3a8SLen Brown }
594f86d3a8SLen Brown 
show_current_governor(struct device * dev,struct device_attribute * attr,char * buf)608a25a2fdSKay Sievers static ssize_t show_current_governor(struct device *dev,
618a25a2fdSKay Sievers 				     struct device_attribute *attr,
6266198f36SRabin Vincent 				     char *buf)
634f86d3a8SLen Brown {
644f86d3a8SLen Brown 	ssize_t ret;
654f86d3a8SLen Brown 
664f86d3a8SLen Brown 	mutex_lock(&cpuidle_lock);
674f86d3a8SLen Brown 	if (cpuidle_curr_governor)
684f86d3a8SLen Brown 		ret = sprintf(buf, "%s\n", cpuidle_curr_governor->name);
694f86d3a8SLen Brown 	else
704f86d3a8SLen Brown 		ret = sprintf(buf, "none\n");
714f86d3a8SLen Brown 	mutex_unlock(&cpuidle_lock);
724f86d3a8SLen Brown 
734f86d3a8SLen Brown 	return ret;
744f86d3a8SLen Brown }
754f86d3a8SLen Brown 
store_current_governor(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)768a25a2fdSKay Sievers static ssize_t store_current_governor(struct device *dev,
778a25a2fdSKay Sievers 				      struct device_attribute *attr,
784f86d3a8SLen Brown 				      const char *buf, size_t count)
794f86d3a8SLen Brown {
80ef7e7d65SHanjun Guo 	char gov_name[CPUIDLE_NAME_LEN + 1];
81ef7e7d65SHanjun Guo 	int ret;
824f86d3a8SLen Brown 	struct cpuidle_governor *gov;
834f86d3a8SLen Brown 
84ef7e7d65SHanjun Guo 	ret = sscanf(buf, "%" __stringify(CPUIDLE_NAME_LEN) "s", gov_name);
85ef7e7d65SHanjun Guo 	if (ret != 1)
864f86d3a8SLen Brown 		return -EINVAL;
874f86d3a8SLen Brown 
884f86d3a8SLen Brown 	mutex_lock(&cpuidle_lock);
89ef7e7d65SHanjun Guo 	ret = -EINVAL;
904f86d3a8SLen Brown 	list_for_each_entry(gov, &cpuidle_governors, governor_list) {
91ef7e7d65SHanjun Guo 		if (!strncmp(gov->name, gov_name, CPUIDLE_NAME_LEN)) {
924f86d3a8SLen Brown 			ret = cpuidle_switch_governor(gov);
934f86d3a8SLen Brown 			break;
944f86d3a8SLen Brown 		}
954f86d3a8SLen Brown 	}
964f86d3a8SLen Brown 	mutex_unlock(&cpuidle_lock);
974f86d3a8SLen Brown 
98ef7e7d65SHanjun Guo 	return ret ? ret : count;
994f86d3a8SLen Brown }
1004f86d3a8SLen Brown 
101b52e93e4SHanjun Guo static DEVICE_ATTR(available_governors, 0444, show_available_governors, NULL);
1028a25a2fdSKay Sievers static DEVICE_ATTR(current_driver, 0444, show_current_driver, NULL);
103b52e93e4SHanjun Guo static DEVICE_ATTR(current_governor, 0644, show_current_governor,
104b52e93e4SHanjun Guo 				   store_current_governor);
1058a25a2fdSKay Sievers static DEVICE_ATTR(current_governor_ro, 0444, show_current_governor, NULL);
1064f86d3a8SLen Brown 
107cce55cc9SHanjun Guo static struct attribute *cpuidle_attrs[] = {
108b52e93e4SHanjun Guo 	&dev_attr_available_governors.attr,
1098a25a2fdSKay Sievers 	&dev_attr_current_driver.attr,
110b52e93e4SHanjun Guo 	&dev_attr_current_governor.attr,
1118a25a2fdSKay Sievers 	&dev_attr_current_governor_ro.attr,
1124f86d3a8SLen Brown 	NULL
1134f86d3a8SLen Brown };
1144f86d3a8SLen Brown 
1158a25a2fdSKay Sievers static struct attribute_group cpuidle_attr_group = {
116cce55cc9SHanjun Guo 	.attrs = cpuidle_attrs,
1174f86d3a8SLen Brown 	.name = "cpuidle",
1184f86d3a8SLen Brown };
1194f86d3a8SLen Brown 
1204f86d3a8SLen Brown /**
1218a25a2fdSKay Sievers  * cpuidle_add_interface - add CPU global sysfs attributes
1224f86d3a8SLen Brown  */
cpuidle_add_interface(void)123*bf6479dbSGreg Kroah-Hartman int cpuidle_add_interface(void)
1244f86d3a8SLen Brown {
125*bf6479dbSGreg Kroah-Hartman 	struct device *dev_root = bus_get_dev_root(&cpu_subsys);
126*bf6479dbSGreg Kroah-Hartman 	int retval;
127*bf6479dbSGreg Kroah-Hartman 
128*bf6479dbSGreg Kroah-Hartman 	if (!dev_root)
129*bf6479dbSGreg Kroah-Hartman 		return -EINVAL;
130*bf6479dbSGreg Kroah-Hartman 
131*bf6479dbSGreg Kroah-Hartman 	retval = sysfs_create_group(&dev_root->kobj, &cpuidle_attr_group);
132*bf6479dbSGreg Kroah-Hartman 	put_device(dev_root);
133*bf6479dbSGreg Kroah-Hartman 	return retval;
1344f86d3a8SLen Brown }
1354f86d3a8SLen Brown 
1364f86d3a8SLen Brown /**
1378a25a2fdSKay Sievers  * cpuidle_remove_interface - remove CPU global sysfs attributes
138a09da3fbSBenjamin Gaignard  * @dev: the target device
1394f86d3a8SLen Brown  */
cpuidle_remove_interface(struct device * dev)1408a25a2fdSKay Sievers void cpuidle_remove_interface(struct device *dev)
1414f86d3a8SLen Brown {
1428a25a2fdSKay Sievers 	sysfs_remove_group(&dev->kobj, &cpuidle_attr_group);
1434f86d3a8SLen Brown }
1444f86d3a8SLen Brown 
1454f86d3a8SLen Brown struct cpuidle_attr {
1464f86d3a8SLen Brown 	struct attribute attr;
1474f86d3a8SLen Brown 	ssize_t (*show)(struct cpuidle_device *, char *);
1484f86d3a8SLen Brown 	ssize_t (*store)(struct cpuidle_device *, const char *, size_t count);
1494f86d3a8SLen Brown };
1504f86d3a8SLen Brown 
1514f86d3a8SLen Brown #define attr_to_cpuidleattr(a) container_of(a, struct cpuidle_attr, attr)
152f89ae89eSDaniel Lezcano 
153728ce22bSDaniel Lezcano struct cpuidle_device_kobj {
154728ce22bSDaniel Lezcano 	struct cpuidle_device *dev;
155728ce22bSDaniel Lezcano 	struct completion kobj_unregister;
156728ce22bSDaniel Lezcano 	struct kobject kobj;
157728ce22bSDaniel Lezcano };
158728ce22bSDaniel Lezcano 
to_cpuidle_device(struct kobject * kobj)159728ce22bSDaniel Lezcano static inline struct cpuidle_device *to_cpuidle_device(struct kobject *kobj)
160728ce22bSDaniel Lezcano {
161728ce22bSDaniel Lezcano 	struct cpuidle_device_kobj *kdev =
162728ce22bSDaniel Lezcano 		container_of(kobj, struct cpuidle_device_kobj, kobj);
163728ce22bSDaniel Lezcano 
164728ce22bSDaniel Lezcano 	return kdev->dev;
165728ce22bSDaniel Lezcano }
166728ce22bSDaniel Lezcano 
cpuidle_show(struct kobject * kobj,struct attribute * attr,char * buf)167f89ae89eSDaniel Lezcano static ssize_t cpuidle_show(struct kobject *kobj, struct attribute *attr,
168f89ae89eSDaniel Lezcano 			    char *buf)
1694f86d3a8SLen Brown {
1704f86d3a8SLen Brown 	int ret = -EIO;
171728ce22bSDaniel Lezcano 	struct cpuidle_device *dev = to_cpuidle_device(kobj);
1724f86d3a8SLen Brown 	struct cpuidle_attr *cattr = attr_to_cpuidleattr(attr);
1734f86d3a8SLen Brown 
1744f86d3a8SLen Brown 	if (cattr->show) {
1754f86d3a8SLen Brown 		mutex_lock(&cpuidle_lock);
1764f86d3a8SLen Brown 		ret = cattr->show(dev, buf);
1774f86d3a8SLen Brown 		mutex_unlock(&cpuidle_lock);
1784f86d3a8SLen Brown 	}
1794f86d3a8SLen Brown 	return ret;
1804f86d3a8SLen Brown }
1814f86d3a8SLen Brown 
cpuidle_store(struct kobject * kobj,struct attribute * attr,const char * buf,size_t count)1824f86d3a8SLen Brown static ssize_t cpuidle_store(struct kobject *kobj, struct attribute *attr,
1834f86d3a8SLen Brown 			     const char *buf, size_t count)
1844f86d3a8SLen Brown {
1854f86d3a8SLen Brown 	int ret = -EIO;
186728ce22bSDaniel Lezcano 	struct cpuidle_device *dev = to_cpuidle_device(kobj);
1874f86d3a8SLen Brown 	struct cpuidle_attr *cattr = attr_to_cpuidleattr(attr);
1884f86d3a8SLen Brown 
1894f86d3a8SLen Brown 	if (cattr->store) {
1904f86d3a8SLen Brown 		mutex_lock(&cpuidle_lock);
1914f86d3a8SLen Brown 		ret = cattr->store(dev, buf, count);
1924f86d3a8SLen Brown 		mutex_unlock(&cpuidle_lock);
1934f86d3a8SLen Brown 	}
1944f86d3a8SLen Brown 	return ret;
1954f86d3a8SLen Brown }
1964f86d3a8SLen Brown 
19752cf25d0SEmese Revfy static const struct sysfs_ops cpuidle_sysfs_ops = {
1984f86d3a8SLen Brown 	.show = cpuidle_show,
1994f86d3a8SLen Brown 	.store = cpuidle_store,
2004f86d3a8SLen Brown };
2014f86d3a8SLen Brown 
cpuidle_sysfs_release(struct kobject * kobj)2024f86d3a8SLen Brown static void cpuidle_sysfs_release(struct kobject *kobj)
2034f86d3a8SLen Brown {
204728ce22bSDaniel Lezcano 	struct cpuidle_device_kobj *kdev =
205728ce22bSDaniel Lezcano 		container_of(kobj, struct cpuidle_device_kobj, kobj);
2064f86d3a8SLen Brown 
207728ce22bSDaniel Lezcano 	complete(&kdev->kobj_unregister);
2084f86d3a8SLen Brown }
2094f86d3a8SLen Brown 
210e898b07dSThomas Weißschuh static const struct kobj_type ktype_cpuidle = {
2114f86d3a8SLen Brown 	.sysfs_ops = &cpuidle_sysfs_ops,
2124f86d3a8SLen Brown 	.release = cpuidle_sysfs_release,
2134f86d3a8SLen Brown };
2144f86d3a8SLen Brown 
2154f86d3a8SLen Brown struct cpuidle_state_attr {
2164f86d3a8SLen Brown 	struct attribute attr;
2174202735eSDeepthi Dharwar 	ssize_t (*show)(struct cpuidle_state *, \
2184202735eSDeepthi Dharwar 					struct cpuidle_state_usage *, char *);
219dc7fd275SShuoX Liu 	ssize_t (*store)(struct cpuidle_state *, \
220dc7fd275SShuoX Liu 			struct cpuidle_state_usage *, const char *, size_t);
2214f86d3a8SLen Brown };
2224f86d3a8SLen Brown 
2234f86d3a8SLen Brown #define define_one_state_ro(_name, show) \
2244f86d3a8SLen Brown static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0444, show, NULL)
2254f86d3a8SLen Brown 
2263a53396bSShuoX Liu #define define_one_state_rw(_name, show, store) \
2273a53396bSShuoX Liu static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0644, show, store)
2283a53396bSShuoX Liu 
2294f86d3a8SLen Brown #define define_show_state_function(_name) \
2304202735eSDeepthi Dharwar static ssize_t show_state_##_name(struct cpuidle_state *state, \
2314202735eSDeepthi Dharwar 			 struct cpuidle_state_usage *state_usage, char *buf) \
2324f86d3a8SLen Brown { \
2334f86d3a8SLen Brown 	return sprintf(buf, "%u\n", state->_name);\
2344f86d3a8SLen Brown }
2354f86d3a8SLen Brown 
2368b78cf60SYi Yang #define define_show_state_ull_function(_name) \
2374202735eSDeepthi Dharwar static ssize_t show_state_##_name(struct cpuidle_state *state, \
238f89ae89eSDaniel Lezcano 				  struct cpuidle_state_usage *state_usage, \
239f89ae89eSDaniel Lezcano 				  char *buf)				\
2408b78cf60SYi Yang { \
2414202735eSDeepthi Dharwar 	return sprintf(buf, "%llu\n", state_usage->_name);\
2428b78cf60SYi Yang }
2438b78cf60SYi Yang 
2444fcb2fcdSVenkatesh Pallipadi #define define_show_state_str_function(_name) \
2454202735eSDeepthi Dharwar static ssize_t show_state_##_name(struct cpuidle_state *state, \
246f89ae89eSDaniel Lezcano 				  struct cpuidle_state_usage *state_usage, \
247f89ae89eSDaniel Lezcano 				  char *buf)				\
2484fcb2fcdSVenkatesh Pallipadi { \
2494fcb2fcdSVenkatesh Pallipadi 	if (state->_name[0] == '\0')\
2504fcb2fcdSVenkatesh Pallipadi 		return sprintf(buf, "<null>\n");\
2514fcb2fcdSVenkatesh Pallipadi 	return sprintf(buf, "%s\n", state->_name);\
2524f86d3a8SLen Brown }
2534f86d3a8SLen Brown 
254c1d51f68SRafael J. Wysocki #define define_show_state_time_function(_name) \
255c1d51f68SRafael J. Wysocki static ssize_t show_state_##_name(struct cpuidle_state *state, \
256c1d51f68SRafael J. Wysocki 				  struct cpuidle_state_usage *state_usage, \
257c1d51f68SRafael J. Wysocki 				  char *buf) \
258c1d51f68SRafael J. Wysocki { \
259c1d51f68SRafael J. Wysocki 	return sprintf(buf, "%llu\n", ktime_to_us(state->_name##_ns)); \
260c1d51f68SRafael J. Wysocki }
261c1d51f68SRafael J. Wysocki 
262c1d51f68SRafael J. Wysocki define_show_state_time_function(exit_latency)
define_show_state_time_function(target_residency)263c1d51f68SRafael J. Wysocki define_show_state_time_function(target_residency)
2644f86d3a8SLen Brown define_show_state_function(power_usage)
2658b78cf60SYi Yang define_show_state_ull_function(usage)
266f49735f4SLina Iyer define_show_state_ull_function(rejected)
2674fcb2fcdSVenkatesh Pallipadi define_show_state_str_function(name)
2684fcb2fcdSVenkatesh Pallipadi define_show_state_str_function(desc)
26904dab58aSRafael J. Wysocki define_show_state_ull_function(above)
27004dab58aSRafael J. Wysocki define_show_state_ull_function(below)
2714fcb2fcdSVenkatesh Pallipadi 
272c1d51f68SRafael J. Wysocki static ssize_t show_state_time(struct cpuidle_state *state,
273c1d51f68SRafael J. Wysocki 			       struct cpuidle_state_usage *state_usage,
274c1d51f68SRafael J. Wysocki 			       char *buf)
275c1d51f68SRafael J. Wysocki {
276c1d51f68SRafael J. Wysocki 	return sprintf(buf, "%llu\n", ktime_to_us(state_usage->time_ns));
277c1d51f68SRafael J. Wysocki }
278c1d51f68SRafael J. Wysocki 
show_state_disable(struct cpuidle_state * state,struct cpuidle_state_usage * state_usage,char * buf)27999e98d3fSRafael J. Wysocki static ssize_t show_state_disable(struct cpuidle_state *state,
28099e98d3fSRafael J. Wysocki 				  struct cpuidle_state_usage *state_usage,
28199e98d3fSRafael J. Wysocki 				  char *buf)
28299e98d3fSRafael J. Wysocki {
28399e98d3fSRafael J. Wysocki 	return sprintf(buf, "%llu\n",
28499e98d3fSRafael J. Wysocki 		       state_usage->disable & CPUIDLE_STATE_DISABLED_BY_USER);
28599e98d3fSRafael J. Wysocki }
28699e98d3fSRafael J. Wysocki 
store_state_disable(struct cpuidle_state * state,struct cpuidle_state_usage * state_usage,const char * buf,size_t size)28799e98d3fSRafael J. Wysocki static ssize_t store_state_disable(struct cpuidle_state *state,
28899e98d3fSRafael J. Wysocki 				   struct cpuidle_state_usage *state_usage,
28999e98d3fSRafael J. Wysocki 				   const char *buf, size_t size)
29099e98d3fSRafael J. Wysocki {
29199e98d3fSRafael J. Wysocki 	unsigned int value;
29299e98d3fSRafael J. Wysocki 	int err;
29399e98d3fSRafael J. Wysocki 
29499e98d3fSRafael J. Wysocki 	if (!capable(CAP_SYS_ADMIN))
29599e98d3fSRafael J. Wysocki 		return -EPERM;
29699e98d3fSRafael J. Wysocki 
29799e98d3fSRafael J. Wysocki 	err = kstrtouint(buf, 0, &value);
29899e98d3fSRafael J. Wysocki 	if (err)
29999e98d3fSRafael J. Wysocki 		return err;
30099e98d3fSRafael J. Wysocki 
30199e98d3fSRafael J. Wysocki 	if (value)
30299e98d3fSRafael J. Wysocki 		state_usage->disable |= CPUIDLE_STATE_DISABLED_BY_USER;
30399e98d3fSRafael J. Wysocki 	else
30499e98d3fSRafael J. Wysocki 		state_usage->disable &= ~CPUIDLE_STATE_DISABLED_BY_USER;
30599e98d3fSRafael J. Wysocki 
30699e98d3fSRafael J. Wysocki 	return size;
30799e98d3fSRafael J. Wysocki }
30899e98d3fSRafael J. Wysocki 
show_state_default_status(struct cpuidle_state * state,struct cpuidle_state_usage * state_usage,char * buf)30975a80267SRafael J. Wysocki static ssize_t show_state_default_status(struct cpuidle_state *state,
31075a80267SRafael J. Wysocki 					  struct cpuidle_state_usage *state_usage,
31175a80267SRafael J. Wysocki 					  char *buf)
31275a80267SRafael J. Wysocki {
31375a80267SRafael J. Wysocki 	return sprintf(buf, "%s\n",
31475a80267SRafael J. Wysocki 		       state->flags & CPUIDLE_FLAG_OFF ? "disabled" : "enabled");
31575a80267SRafael J. Wysocki }
31675a80267SRafael J. Wysocki 
3174f86d3a8SLen Brown define_one_state_ro(name, show_state_name);
3184fcb2fcdSVenkatesh Pallipadi define_one_state_ro(desc, show_state_desc);
3194f86d3a8SLen Brown define_one_state_ro(latency, show_state_exit_latency);
3209bc0482fSDaniel Lezcano define_one_state_ro(residency, show_state_target_residency);
3214f86d3a8SLen Brown define_one_state_ro(power, show_state_power_usage);
3224f86d3a8SLen Brown define_one_state_ro(usage, show_state_usage);
323f49735f4SLina Iyer define_one_state_ro(rejected, show_state_rejected);
3244f86d3a8SLen Brown define_one_state_ro(time, show_state_time);
3253a53396bSShuoX Liu define_one_state_rw(disable, show_state_disable, store_state_disable);
32604dab58aSRafael J. Wysocki define_one_state_ro(above, show_state_above);
32704dab58aSRafael J. Wysocki define_one_state_ro(below, show_state_below);
32875a80267SRafael J. Wysocki define_one_state_ro(default_status, show_state_default_status);
3294f86d3a8SLen Brown 
3304f86d3a8SLen Brown static struct attribute *cpuidle_state_default_attrs[] = {
3314f86d3a8SLen Brown 	&attr_name.attr,
3324fcb2fcdSVenkatesh Pallipadi 	&attr_desc.attr,
3334f86d3a8SLen Brown 	&attr_latency.attr,
3349bc0482fSDaniel Lezcano 	&attr_residency.attr,
3354f86d3a8SLen Brown 	&attr_power.attr,
3364f86d3a8SLen Brown 	&attr_usage.attr,
337f49735f4SLina Iyer 	&attr_rejected.attr,
3384f86d3a8SLen Brown 	&attr_time.attr,
3393a53396bSShuoX Liu 	&attr_disable.attr,
34004dab58aSRafael J. Wysocki 	&attr_above.attr,
34104dab58aSRafael J. Wysocki 	&attr_below.attr,
34275a80267SRafael J. Wysocki 	&attr_default_status.attr,
3434f86d3a8SLen Brown 	NULL
3444f86d3a8SLen Brown };
3457dfc5b6eSGreg Kroah-Hartman ATTRIBUTE_GROUPS(cpuidle_state_default);
3464f86d3a8SLen Brown 
347349631e0SDaniel Lezcano struct cpuidle_state_kobj {
348349631e0SDaniel Lezcano 	struct cpuidle_state *state;
349349631e0SDaniel Lezcano 	struct cpuidle_state_usage *state_usage;
350349631e0SDaniel Lezcano 	struct completion kobj_unregister;
351349631e0SDaniel Lezcano 	struct kobject kobj;
352259231a0SMarcelo Tosatti 	struct cpuidle_device *device;
353349631e0SDaniel Lezcano };
354349631e0SDaniel Lezcano 
35564bdff69SRafael J. Wysocki #ifdef CONFIG_SUSPEND
35664bdff69SRafael J. Wysocki #define define_show_state_s2idle_ull_function(_name) \
35764bdff69SRafael J. Wysocki static ssize_t show_state_s2idle_##_name(struct cpuidle_state *state, \
35864bdff69SRafael J. Wysocki 					 struct cpuidle_state_usage *state_usage, \
35964bdff69SRafael J. Wysocki 					 char *buf)				\
36064bdff69SRafael J. Wysocki { \
36164bdff69SRafael J. Wysocki 	return sprintf(buf, "%llu\n", state_usage->s2idle_##_name);\
36264bdff69SRafael J. Wysocki }
36364bdff69SRafael J. Wysocki 
36464bdff69SRafael J. Wysocki define_show_state_s2idle_ull_function(usage);
36564bdff69SRafael J. Wysocki define_show_state_s2idle_ull_function(time);
36664bdff69SRafael J. Wysocki 
36764bdff69SRafael J. Wysocki #define define_one_state_s2idle_ro(_name, show) \
36864bdff69SRafael J. Wysocki static struct cpuidle_state_attr attr_s2idle_##_name = \
36964bdff69SRafael J. Wysocki 	__ATTR(_name, 0444, show, NULL)
37064bdff69SRafael J. Wysocki 
37164bdff69SRafael J. Wysocki define_one_state_s2idle_ro(usage, show_state_s2idle_usage);
37264bdff69SRafael J. Wysocki define_one_state_s2idle_ro(time, show_state_s2idle_time);
37364bdff69SRafael J. Wysocki 
37464bdff69SRafael J. Wysocki static struct attribute *cpuidle_state_s2idle_attrs[] = {
37564bdff69SRafael J. Wysocki 	&attr_s2idle_usage.attr,
37664bdff69SRafael J. Wysocki 	&attr_s2idle_time.attr,
37764bdff69SRafael J. Wysocki 	NULL
37864bdff69SRafael J. Wysocki };
37964bdff69SRafael J. Wysocki 
38064bdff69SRafael J. Wysocki static const struct attribute_group cpuidle_state_s2idle_group = {
38164bdff69SRafael J. Wysocki 	.name	= "s2idle",
38264bdff69SRafael J. Wysocki 	.attrs	= cpuidle_state_s2idle_attrs,
38364bdff69SRafael J. Wysocki };
38464bdff69SRafael J. Wysocki 
cpuidle_add_s2idle_attr_group(struct cpuidle_state_kobj * kobj)38564bdff69SRafael J. Wysocki static void cpuidle_add_s2idle_attr_group(struct cpuidle_state_kobj *kobj)
38664bdff69SRafael J. Wysocki {
38764bdff69SRafael J. Wysocki 	int ret;
38864bdff69SRafael J. Wysocki 
38964bdff69SRafael J. Wysocki 	if (!kobj->state->enter_s2idle)
39064bdff69SRafael J. Wysocki 		return;
39164bdff69SRafael J. Wysocki 
39264bdff69SRafael J. Wysocki 	ret = sysfs_create_group(&kobj->kobj, &cpuidle_state_s2idle_group);
39364bdff69SRafael J. Wysocki 	if (ret)
39464bdff69SRafael J. Wysocki 		pr_debug("%s: sysfs attribute group not created\n", __func__);
39564bdff69SRafael J. Wysocki }
39664bdff69SRafael J. Wysocki 
cpuidle_remove_s2idle_attr_group(struct cpuidle_state_kobj * kobj)39764bdff69SRafael J. Wysocki static void cpuidle_remove_s2idle_attr_group(struct cpuidle_state_kobj *kobj)
39864bdff69SRafael J. Wysocki {
39964bdff69SRafael J. Wysocki 	if (kobj->state->enter_s2idle)
40064bdff69SRafael J. Wysocki 		sysfs_remove_group(&kobj->kobj, &cpuidle_state_s2idle_group);
40164bdff69SRafael J. Wysocki }
40264bdff69SRafael J. Wysocki #else
cpuidle_add_s2idle_attr_group(struct cpuidle_state_kobj * kobj)40364bdff69SRafael J. Wysocki static inline void cpuidle_add_s2idle_attr_group(struct cpuidle_state_kobj *kobj) { }
cpuidle_remove_s2idle_attr_group(struct cpuidle_state_kobj * kobj)40464bdff69SRafael J. Wysocki static inline void cpuidle_remove_s2idle_attr_group(struct cpuidle_state_kobj *kobj) { }
40564bdff69SRafael J. Wysocki #endif /* CONFIG_SUSPEND */
40664bdff69SRafael J. Wysocki 
4074f86d3a8SLen Brown #define kobj_to_state_obj(k) container_of(k, struct cpuidle_state_kobj, kobj)
4084f86d3a8SLen Brown #define kobj_to_state(k) (kobj_to_state_obj(k)->state)
4094202735eSDeepthi Dharwar #define kobj_to_state_usage(k) (kobj_to_state_obj(k)->state_usage)
410259231a0SMarcelo Tosatti #define kobj_to_device(k) (kobj_to_state_obj(k)->device)
4114f86d3a8SLen Brown #define attr_to_stateattr(a) container_of(a, struct cpuidle_state_attr, attr)
412f89ae89eSDaniel Lezcano 
cpuidle_state_show(struct kobject * kobj,struct attribute * attr,char * buf)413f89ae89eSDaniel Lezcano static ssize_t cpuidle_state_show(struct kobject *kobj, struct attribute *attr,
414f89ae89eSDaniel Lezcano 				  char *buf)
4154f86d3a8SLen Brown {
4164f86d3a8SLen Brown 	int ret = -EIO;
4174f86d3a8SLen Brown 	struct cpuidle_state *state = kobj_to_state(kobj);
4184202735eSDeepthi Dharwar 	struct cpuidle_state_usage *state_usage = kobj_to_state_usage(kobj);
4194f86d3a8SLen Brown 	struct cpuidle_state_attr *cattr = attr_to_stateattr(attr);
4204f86d3a8SLen Brown 
4214f86d3a8SLen Brown 	if (cattr->show)
4224202735eSDeepthi Dharwar 		ret = cattr->show(state, state_usage, buf);
4234f86d3a8SLen Brown 
4244f86d3a8SLen Brown 	return ret;
4254f86d3a8SLen Brown }
4264f86d3a8SLen Brown 
cpuidle_state_store(struct kobject * kobj,struct attribute * attr,const char * buf,size_t size)427f89ae89eSDaniel Lezcano static ssize_t cpuidle_state_store(struct kobject *kobj, struct attribute *attr,
428f89ae89eSDaniel Lezcano 				   const char *buf, size_t size)
4293a53396bSShuoX Liu {
4303a53396bSShuoX Liu 	int ret = -EIO;
4313a53396bSShuoX Liu 	struct cpuidle_state *state = kobj_to_state(kobj);
432dc7fd275SShuoX Liu 	struct cpuidle_state_usage *state_usage = kobj_to_state_usage(kobj);
4333a53396bSShuoX Liu 	struct cpuidle_state_attr *cattr = attr_to_stateattr(attr);
434259231a0SMarcelo Tosatti 	struct cpuidle_device *dev = kobj_to_device(kobj);
4353a53396bSShuoX Liu 
4363a53396bSShuoX Liu 	if (cattr->store)
437dc7fd275SShuoX Liu 		ret = cattr->store(state, state_usage, buf, size);
4383a53396bSShuoX Liu 
439259231a0SMarcelo Tosatti 	/* reset poll time cache */
440259231a0SMarcelo Tosatti 	dev->poll_limit_ns = 0;
441259231a0SMarcelo Tosatti 
4423a53396bSShuoX Liu 	return ret;
4433a53396bSShuoX Liu }
4443a53396bSShuoX Liu 
44552cf25d0SEmese Revfy static const struct sysfs_ops cpuidle_state_sysfs_ops = {
4464f86d3a8SLen Brown 	.show = cpuidle_state_show,
4473a53396bSShuoX Liu 	.store = cpuidle_state_store,
4484f86d3a8SLen Brown };
4494f86d3a8SLen Brown 
cpuidle_state_sysfs_release(struct kobject * kobj)4504f86d3a8SLen Brown static void cpuidle_state_sysfs_release(struct kobject *kobj)
4514f86d3a8SLen Brown {
4524f86d3a8SLen Brown 	struct cpuidle_state_kobj *state_obj = kobj_to_state_obj(kobj);
4534f86d3a8SLen Brown 
4544f86d3a8SLen Brown 	complete(&state_obj->kobj_unregister);
4554f86d3a8SLen Brown }
4564f86d3a8SLen Brown 
457e898b07dSThomas Weißschuh static const struct kobj_type ktype_state_cpuidle = {
4584f86d3a8SLen Brown 	.sysfs_ops = &cpuidle_state_sysfs_ops,
4597dfc5b6eSGreg Kroah-Hartman 	.default_groups = cpuidle_state_default_groups,
4604f86d3a8SLen Brown 	.release = cpuidle_state_sysfs_release,
4614f86d3a8SLen Brown };
4624f86d3a8SLen Brown 
cpuidle_free_state_kobj(struct cpuidle_device * device,int i)46342b16b3fSJesper Juhl static inline void cpuidle_free_state_kobj(struct cpuidle_device *device, int i)
4644f86d3a8SLen Brown {
46564bdff69SRafael J. Wysocki 	cpuidle_remove_s2idle_attr_group(device->kobjs[i]);
466c10997f6SGreg Kroah-Hartman 	kobject_put(&device->kobjs[i]->kobj);
4674f86d3a8SLen Brown 	wait_for_completion(&device->kobjs[i]->kobj_unregister);
4684f86d3a8SLen Brown 	kfree(device->kobjs[i]);
4694f86d3a8SLen Brown 	device->kobjs[i] = NULL;
4704f86d3a8SLen Brown }
4714f86d3a8SLen Brown 
4724f86d3a8SLen Brown /**
473bf4d1b5dSDaniel Lezcano  * cpuidle_add_state_sysfs - adds cpuidle states sysfs attributes
4744f86d3a8SLen Brown  * @device: the target device
4754f86d3a8SLen Brown  */
cpuidle_add_state_sysfs(struct cpuidle_device * device)476bf4d1b5dSDaniel Lezcano static int cpuidle_add_state_sysfs(struct cpuidle_device *device)
4774f86d3a8SLen Brown {
4784f86d3a8SLen Brown 	int i, ret = -ENOMEM;
4794f86d3a8SLen Brown 	struct cpuidle_state_kobj *kobj;
480728ce22bSDaniel Lezcano 	struct cpuidle_device_kobj *kdev = device->kobj_dev;
481bf4d1b5dSDaniel Lezcano 	struct cpuidle_driver *drv = cpuidle_get_cpu_driver(device);
4824f86d3a8SLen Brown 
4834f86d3a8SLen Brown 	/* state statistics */
484d75e4af1SBartlomiej Zolnierkiewicz 	for (i = 0; i < drv->state_count; i++) {
4854f86d3a8SLen Brown 		kobj = kzalloc(sizeof(struct cpuidle_state_kobj), GFP_KERNEL);
4868f6040ceSPan Bian 		if (!kobj) {
4878f6040ceSPan Bian 			ret = -ENOMEM;
4884f86d3a8SLen Brown 			goto error_state;
4898f6040ceSPan Bian 		}
49046bcfad7SDeepthi Dharwar 		kobj->state = &drv->states[i];
4914202735eSDeepthi Dharwar 		kobj->state_usage = &device->states_usage[i];
492259231a0SMarcelo Tosatti 		kobj->device = device;
4934f86d3a8SLen Brown 		init_completion(&kobj->kobj_unregister);
4944f86d3a8SLen Brown 
495e45a00d6SDaniel Lezcano 		ret = kobject_init_and_add(&kobj->kobj, &ktype_state_cpuidle,
496728ce22bSDaniel Lezcano 					   &kdev->kobj, "state%d", i);
4974f86d3a8SLen Brown 		if (ret) {
498c343bf1bSQiushi Wu 			kobject_put(&kobj->kobj);
499e5f5a66cSAnel Orazgaliyeva 			kfree(kobj);
5004f86d3a8SLen Brown 			goto error_state;
5014f86d3a8SLen Brown 		}
50264bdff69SRafael J. Wysocki 		cpuidle_add_s2idle_attr_group(kobj);
50394f57f33SGreg Kroah-Hartman 		kobject_uevent(&kobj->kobj, KOBJ_ADD);
5044f86d3a8SLen Brown 		device->kobjs[i] = kobj;
5054f86d3a8SLen Brown 	}
5064f86d3a8SLen Brown 
5074f86d3a8SLen Brown 	return 0;
5084f86d3a8SLen Brown 
5094f86d3a8SLen Brown error_state:
5104f86d3a8SLen Brown 	for (i = i - 1; i >= 0; i--)
5114f86d3a8SLen Brown 		cpuidle_free_state_kobj(device, i);
5124f86d3a8SLen Brown 	return ret;
5134f86d3a8SLen Brown }
5144f86d3a8SLen Brown 
5154f86d3a8SLen Brown /**
516d00ebcc6SYang Li  * cpuidle_remove_state_sysfs - removes the cpuidle states sysfs attributes
5174f86d3a8SLen Brown  * @device: the target device
5184f86d3a8SLen Brown  */
cpuidle_remove_state_sysfs(struct cpuidle_device * device)519bf4d1b5dSDaniel Lezcano static void cpuidle_remove_state_sysfs(struct cpuidle_device *device)
5204f86d3a8SLen Brown {
521d75e4af1SBartlomiej Zolnierkiewicz 	struct cpuidle_driver *drv = cpuidle_get_cpu_driver(device);
5224f86d3a8SLen Brown 	int i;
5234f86d3a8SLen Brown 
524d75e4af1SBartlomiej Zolnierkiewicz 	for (i = 0; i < drv->state_count; i++)
5254f86d3a8SLen Brown 		cpuidle_free_state_kobj(device, i);
5264f86d3a8SLen Brown }
5274f86d3a8SLen Brown 
528bf4d1b5dSDaniel Lezcano #ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS
529bf4d1b5dSDaniel Lezcano #define kobj_to_driver_kobj(k) container_of(k, struct cpuidle_driver_kobj, kobj)
530bf4d1b5dSDaniel Lezcano #define attr_to_driver_attr(a) container_of(a, struct cpuidle_driver_attr, attr)
531bf4d1b5dSDaniel Lezcano 
532bf4d1b5dSDaniel Lezcano #define define_one_driver_ro(_name, show)                       \
533bf4d1b5dSDaniel Lezcano 	static struct cpuidle_driver_attr attr_driver_##_name = \
5344f8eea9bSMohammad Merajul Islam Molla 		__ATTR(_name, 0444, show, NULL)
535bf4d1b5dSDaniel Lezcano 
536bf4d1b5dSDaniel Lezcano struct cpuidle_driver_kobj {
537bf4d1b5dSDaniel Lezcano 	struct cpuidle_driver *drv;
538bf4d1b5dSDaniel Lezcano 	struct completion kobj_unregister;
539bf4d1b5dSDaniel Lezcano 	struct kobject kobj;
540bf4d1b5dSDaniel Lezcano };
541bf4d1b5dSDaniel Lezcano 
542bf4d1b5dSDaniel Lezcano struct cpuidle_driver_attr {
543bf4d1b5dSDaniel Lezcano 	struct attribute attr;
544bf4d1b5dSDaniel Lezcano 	ssize_t (*show)(struct cpuidle_driver *, char *);
545bf4d1b5dSDaniel Lezcano 	ssize_t (*store)(struct cpuidle_driver *, const char *, size_t);
546bf4d1b5dSDaniel Lezcano };
547bf4d1b5dSDaniel Lezcano 
show_driver_name(struct cpuidle_driver * drv,char * buf)548bf4d1b5dSDaniel Lezcano static ssize_t show_driver_name(struct cpuidle_driver *drv, char *buf)
549bf4d1b5dSDaniel Lezcano {
550bf4d1b5dSDaniel Lezcano 	ssize_t ret;
551bf4d1b5dSDaniel Lezcano 
552bf4d1b5dSDaniel Lezcano 	spin_lock(&cpuidle_driver_lock);
553bf4d1b5dSDaniel Lezcano 	ret = sprintf(buf, "%s\n", drv ? drv->name : "none");
554bf4d1b5dSDaniel Lezcano 	spin_unlock(&cpuidle_driver_lock);
555bf4d1b5dSDaniel Lezcano 
556bf4d1b5dSDaniel Lezcano 	return ret;
557bf4d1b5dSDaniel Lezcano }
558bf4d1b5dSDaniel Lezcano 
cpuidle_driver_sysfs_release(struct kobject * kobj)559bf4d1b5dSDaniel Lezcano static void cpuidle_driver_sysfs_release(struct kobject *kobj)
560bf4d1b5dSDaniel Lezcano {
561bf4d1b5dSDaniel Lezcano 	struct cpuidle_driver_kobj *driver_kobj = kobj_to_driver_kobj(kobj);
562bf4d1b5dSDaniel Lezcano 	complete(&driver_kobj->kobj_unregister);
563bf4d1b5dSDaniel Lezcano }
564bf4d1b5dSDaniel Lezcano 
cpuidle_driver_show(struct kobject * kobj,struct attribute * attr,char * buf)565bf4d1b5dSDaniel Lezcano static ssize_t cpuidle_driver_show(struct kobject *kobj, struct attribute *attr,
566bf4d1b5dSDaniel Lezcano 				   char *buf)
567bf4d1b5dSDaniel Lezcano {
568bf4d1b5dSDaniel Lezcano 	int ret = -EIO;
569bf4d1b5dSDaniel Lezcano 	struct cpuidle_driver_kobj *driver_kobj = kobj_to_driver_kobj(kobj);
570bf4d1b5dSDaniel Lezcano 	struct cpuidle_driver_attr *dattr = attr_to_driver_attr(attr);
571bf4d1b5dSDaniel Lezcano 
572bf4d1b5dSDaniel Lezcano 	if (dattr->show)
573bf4d1b5dSDaniel Lezcano 		ret = dattr->show(driver_kobj->drv, buf);
574bf4d1b5dSDaniel Lezcano 
575bf4d1b5dSDaniel Lezcano 	return ret;
576bf4d1b5dSDaniel Lezcano }
577bf4d1b5dSDaniel Lezcano 
cpuidle_driver_store(struct kobject * kobj,struct attribute * attr,const char * buf,size_t size)578bf4d1b5dSDaniel Lezcano static ssize_t cpuidle_driver_store(struct kobject *kobj, struct attribute *attr,
579bf4d1b5dSDaniel Lezcano 				    const char *buf, size_t size)
580bf4d1b5dSDaniel Lezcano {
581bf4d1b5dSDaniel Lezcano 	int ret = -EIO;
582bf4d1b5dSDaniel Lezcano 	struct cpuidle_driver_kobj *driver_kobj = kobj_to_driver_kobj(kobj);
583bf4d1b5dSDaniel Lezcano 	struct cpuidle_driver_attr *dattr = attr_to_driver_attr(attr);
584bf4d1b5dSDaniel Lezcano 
585bf4d1b5dSDaniel Lezcano 	if (dattr->store)
586bf4d1b5dSDaniel Lezcano 		ret = dattr->store(driver_kobj->drv, buf, size);
587bf4d1b5dSDaniel Lezcano 
588bf4d1b5dSDaniel Lezcano 	return ret;
589bf4d1b5dSDaniel Lezcano }
590bf4d1b5dSDaniel Lezcano 
591bf4d1b5dSDaniel Lezcano define_one_driver_ro(name, show_driver_name);
592bf4d1b5dSDaniel Lezcano 
593bf4d1b5dSDaniel Lezcano static const struct sysfs_ops cpuidle_driver_sysfs_ops = {
594bf4d1b5dSDaniel Lezcano 	.show = cpuidle_driver_show,
595bf4d1b5dSDaniel Lezcano 	.store = cpuidle_driver_store,
596bf4d1b5dSDaniel Lezcano };
597bf4d1b5dSDaniel Lezcano 
598bf4d1b5dSDaniel Lezcano static struct attribute *cpuidle_driver_default_attrs[] = {
599bf4d1b5dSDaniel Lezcano 	&attr_driver_name.attr,
600bf4d1b5dSDaniel Lezcano 	NULL
601bf4d1b5dSDaniel Lezcano };
6027dfc5b6eSGreg Kroah-Hartman ATTRIBUTE_GROUPS(cpuidle_driver_default);
603bf4d1b5dSDaniel Lezcano 
604e898b07dSThomas Weißschuh static const struct kobj_type ktype_driver_cpuidle = {
605bf4d1b5dSDaniel Lezcano 	.sysfs_ops = &cpuidle_driver_sysfs_ops,
6067dfc5b6eSGreg Kroah-Hartman 	.default_groups = cpuidle_driver_default_groups,
607bf4d1b5dSDaniel Lezcano 	.release = cpuidle_driver_sysfs_release,
608bf4d1b5dSDaniel Lezcano };
609bf4d1b5dSDaniel Lezcano 
610bf4d1b5dSDaniel Lezcano /**
611bf4d1b5dSDaniel Lezcano  * cpuidle_add_driver_sysfs - adds the driver name sysfs attribute
612a09da3fbSBenjamin Gaignard  * @dev: the target device
613bf4d1b5dSDaniel Lezcano  */
cpuidle_add_driver_sysfs(struct cpuidle_device * dev)614bf4d1b5dSDaniel Lezcano static int cpuidle_add_driver_sysfs(struct cpuidle_device *dev)
615bf4d1b5dSDaniel Lezcano {
616bf4d1b5dSDaniel Lezcano 	struct cpuidle_driver_kobj *kdrv;
617728ce22bSDaniel Lezcano 	struct cpuidle_device_kobj *kdev = dev->kobj_dev;
618bf4d1b5dSDaniel Lezcano 	struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);
619bf4d1b5dSDaniel Lezcano 	int ret;
620bf4d1b5dSDaniel Lezcano 
621bf4d1b5dSDaniel Lezcano 	kdrv = kzalloc(sizeof(*kdrv), GFP_KERNEL);
622bf4d1b5dSDaniel Lezcano 	if (!kdrv)
623bf4d1b5dSDaniel Lezcano 		return -ENOMEM;
624bf4d1b5dSDaniel Lezcano 
625bf4d1b5dSDaniel Lezcano 	kdrv->drv = drv;
626bf4d1b5dSDaniel Lezcano 	init_completion(&kdrv->kobj_unregister);
627bf4d1b5dSDaniel Lezcano 
628bf4d1b5dSDaniel Lezcano 	ret = kobject_init_and_add(&kdrv->kobj, &ktype_driver_cpuidle,
629728ce22bSDaniel Lezcano 				   &kdev->kobj, "driver");
630bf4d1b5dSDaniel Lezcano 	if (ret) {
631c343bf1bSQiushi Wu 		kobject_put(&kdrv->kobj);
632e5f5a66cSAnel Orazgaliyeva 		kfree(kdrv);
633bf4d1b5dSDaniel Lezcano 		return ret;
634bf4d1b5dSDaniel Lezcano 	}
635bf4d1b5dSDaniel Lezcano 
636bf4d1b5dSDaniel Lezcano 	kobject_uevent(&kdrv->kobj, KOBJ_ADD);
637bf4d1b5dSDaniel Lezcano 	dev->kobj_driver = kdrv;
638bf4d1b5dSDaniel Lezcano 
639bf4d1b5dSDaniel Lezcano 	return ret;
640bf4d1b5dSDaniel Lezcano }
641bf4d1b5dSDaniel Lezcano 
642bf4d1b5dSDaniel Lezcano /**
643bf4d1b5dSDaniel Lezcano  * cpuidle_remove_driver_sysfs - removes the driver name sysfs attribute
644a09da3fbSBenjamin Gaignard  * @dev: the target device
645bf4d1b5dSDaniel Lezcano  */
cpuidle_remove_driver_sysfs(struct cpuidle_device * dev)646bf4d1b5dSDaniel Lezcano static void cpuidle_remove_driver_sysfs(struct cpuidle_device *dev)
647bf4d1b5dSDaniel Lezcano {
648bf4d1b5dSDaniel Lezcano 	struct cpuidle_driver_kobj *kdrv = dev->kobj_driver;
649bf4d1b5dSDaniel Lezcano 	kobject_put(&kdrv->kobj);
650bf4d1b5dSDaniel Lezcano 	wait_for_completion(&kdrv->kobj_unregister);
651bf4d1b5dSDaniel Lezcano 	kfree(kdrv);
652bf4d1b5dSDaniel Lezcano }
653bf4d1b5dSDaniel Lezcano #else
cpuidle_add_driver_sysfs(struct cpuidle_device * dev)654bf4d1b5dSDaniel Lezcano static inline int cpuidle_add_driver_sysfs(struct cpuidle_device *dev)
655bf4d1b5dSDaniel Lezcano {
656bf4d1b5dSDaniel Lezcano 	return 0;
657bf4d1b5dSDaniel Lezcano }
658bf4d1b5dSDaniel Lezcano 
cpuidle_remove_driver_sysfs(struct cpuidle_device * dev)659bf4d1b5dSDaniel Lezcano static inline void cpuidle_remove_driver_sysfs(struct cpuidle_device *dev)
660bf4d1b5dSDaniel Lezcano {
661bf4d1b5dSDaniel Lezcano 	;
662bf4d1b5dSDaniel Lezcano }
663bf4d1b5dSDaniel Lezcano #endif
664bf4d1b5dSDaniel Lezcano 
665bf4d1b5dSDaniel Lezcano /**
666bf4d1b5dSDaniel Lezcano  * cpuidle_add_device_sysfs - adds device specific sysfs attributes
667bf4d1b5dSDaniel Lezcano  * @device: the target device
668bf4d1b5dSDaniel Lezcano  */
cpuidle_add_device_sysfs(struct cpuidle_device * device)669bf4d1b5dSDaniel Lezcano int cpuidle_add_device_sysfs(struct cpuidle_device *device)
670bf4d1b5dSDaniel Lezcano {
671bf4d1b5dSDaniel Lezcano 	int ret;
672bf4d1b5dSDaniel Lezcano 
673bf4d1b5dSDaniel Lezcano 	ret = cpuidle_add_state_sysfs(device);
674bf4d1b5dSDaniel Lezcano 	if (ret)
675bf4d1b5dSDaniel Lezcano 		return ret;
676bf4d1b5dSDaniel Lezcano 
677bf4d1b5dSDaniel Lezcano 	ret = cpuidle_add_driver_sysfs(device);
678bf4d1b5dSDaniel Lezcano 	if (ret)
679bf4d1b5dSDaniel Lezcano 		cpuidle_remove_state_sysfs(device);
680bf4d1b5dSDaniel Lezcano 	return ret;
681bf4d1b5dSDaniel Lezcano }
682bf4d1b5dSDaniel Lezcano 
683bf4d1b5dSDaniel Lezcano /**
684bf4d1b5dSDaniel Lezcano  * cpuidle_remove_device_sysfs : removes device specific sysfs attributes
685bf4d1b5dSDaniel Lezcano  * @device : the target device
686bf4d1b5dSDaniel Lezcano  */
cpuidle_remove_device_sysfs(struct cpuidle_device * device)687bf4d1b5dSDaniel Lezcano void cpuidle_remove_device_sysfs(struct cpuidle_device *device)
688bf4d1b5dSDaniel Lezcano {
689bf4d1b5dSDaniel Lezcano 	cpuidle_remove_driver_sysfs(device);
690bf4d1b5dSDaniel Lezcano 	cpuidle_remove_state_sysfs(device);
691bf4d1b5dSDaniel Lezcano }
692bf4d1b5dSDaniel Lezcano 
6934f86d3a8SLen Brown /**
6944f86d3a8SLen Brown  * cpuidle_add_sysfs - creates a sysfs instance for the target device
6958a25a2fdSKay Sievers  * @dev: the target device
6964f86d3a8SLen Brown  */
cpuidle_add_sysfs(struct cpuidle_device * dev)6971aef40e2SDaniel Lezcano int cpuidle_add_sysfs(struct cpuidle_device *dev)
6984f86d3a8SLen Brown {
699728ce22bSDaniel Lezcano 	struct cpuidle_device_kobj *kdev;
7001aef40e2SDaniel Lezcano 	struct device *cpu_dev = get_cpu_device((unsigned long)dev->cpu);
70194f57f33SGreg Kroah-Hartman 	int error;
7024f86d3a8SLen Brown 
703ad0a45fdSVaidyanathan Srinivasan 	/*
704ad0a45fdSVaidyanathan Srinivasan 	 * Return if cpu_device is not setup for this CPU.
705ad0a45fdSVaidyanathan Srinivasan 	 *
706ad0a45fdSVaidyanathan Srinivasan 	 * This could happen if the arch did not set up cpu_device
707ad0a45fdSVaidyanathan Srinivasan 	 * since this CPU is not in cpu_present mask and the
708ad0a45fdSVaidyanathan Srinivasan 	 * driver did not send a correct CPU mask during registration.
709ad0a45fdSVaidyanathan Srinivasan 	 * Without this check we would end up passing bogus
710ad0a45fdSVaidyanathan Srinivasan 	 * value for &cpu_dev->kobj in kobject_init_and_add()
711ad0a45fdSVaidyanathan Srinivasan 	 */
712ad0a45fdSVaidyanathan Srinivasan 	if (!cpu_dev)
713ad0a45fdSVaidyanathan Srinivasan 		return -ENODEV;
714ad0a45fdSVaidyanathan Srinivasan 
715728ce22bSDaniel Lezcano 	kdev = kzalloc(sizeof(*kdev), GFP_KERNEL);
716728ce22bSDaniel Lezcano 	if (!kdev)
717728ce22bSDaniel Lezcano 		return -ENOMEM;
718728ce22bSDaniel Lezcano 	kdev->dev = dev;
719e45a00d6SDaniel Lezcano 
720728ce22bSDaniel Lezcano 	init_completion(&kdev->kobj_unregister);
721728ce22bSDaniel Lezcano 
722728ce22bSDaniel Lezcano 	error = kobject_init_and_add(&kdev->kobj, &ktype_cpuidle, &cpu_dev->kobj,
72394f57f33SGreg Kroah-Hartman 				   "cpuidle");
724728ce22bSDaniel Lezcano 	if (error) {
725c343bf1bSQiushi Wu 		kobject_put(&kdev->kobj);
726e5f5a66cSAnel Orazgaliyeva 		kfree(kdev);
72794f57f33SGreg Kroah-Hartman 		return error;
7284f86d3a8SLen Brown 	}
7294f86d3a8SLen Brown 
730e5f5a66cSAnel Orazgaliyeva 	dev->kobj_dev = kdev;
731728ce22bSDaniel Lezcano 	kobject_uevent(&kdev->kobj, KOBJ_ADD);
732728ce22bSDaniel Lezcano 
733728ce22bSDaniel Lezcano 	return 0;
734728ce22bSDaniel Lezcano }
735728ce22bSDaniel Lezcano 
7364f86d3a8SLen Brown /**
7374f86d3a8SLen Brown  * cpuidle_remove_sysfs - deletes a sysfs instance on the target device
7388a25a2fdSKay Sievers  * @dev: the target device
7394f86d3a8SLen Brown  */
cpuidle_remove_sysfs(struct cpuidle_device * dev)7401aef40e2SDaniel Lezcano void cpuidle_remove_sysfs(struct cpuidle_device *dev)
7414f86d3a8SLen Brown {
742728ce22bSDaniel Lezcano 	struct cpuidle_device_kobj *kdev = dev->kobj_dev;
743728ce22bSDaniel Lezcano 
744728ce22bSDaniel Lezcano 	kobject_put(&kdev->kobj);
745728ce22bSDaniel Lezcano 	wait_for_completion(&kdev->kobj_unregister);
746728ce22bSDaniel Lezcano 	kfree(kdev);
7474f86d3a8SLen Brown }
748