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