* [PATCH 2/9] Thermal: Create zone level APIs
From: Durgadoss R @ 2013-01-07 7:13 UTC (permalink / raw)
To: rui.zhang, linux-pm
Cc: linux-kernel, eduardo.valentin, hongbo.zhang, wni, Durgadoss R
In-Reply-To: <1357542806-20449-1-git-send-email-durgadoss.r@intel.com>
This patch adds a new thermal_zone structure to
thermal.h. Also, adds zone level APIs to the thermal
framework.
A thermal zone is a hot spot on the platform, which
can have one or more sensors and cooling devices attached
to it. These sensors can be mapped to a set of cooling
devices, which when throttled, can help to bring down
the temperature of the hot spot.
Signed-off-by: Durgadoss R <durgadoss.r@intel.com>
---
drivers/thermal/thermal_sys.c | 196 +++++++++++++++++++++++++++++++++++++++++
include/linux/thermal.h | 22 +++++
2 files changed, 218 insertions(+)
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index b2becb9..513b0fc 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -44,19 +44,46 @@ MODULE_DESCRIPTION("Generic thermal management sysfs support");
MODULE_LICENSE("GPL");
static DEFINE_IDR(thermal_tz_idr);
+static DEFINE_IDR(thermal_zone_idr);
static DEFINE_IDR(thermal_cdev_idr);
static DEFINE_IDR(thermal_sensor_idr);
static DEFINE_MUTEX(thermal_idr_lock);
static LIST_HEAD(thermal_tz_list);
static LIST_HEAD(thermal_sensor_list);
+static LIST_HEAD(thermal_zone_list);
static LIST_HEAD(thermal_cdev_list);
static LIST_HEAD(thermal_governor_list);
static DEFINE_MUTEX(thermal_list_lock);
static DEFINE_MUTEX(sensor_list_lock);
+static DEFINE_MUTEX(zone_list_lock);
static DEFINE_MUTEX(thermal_governor_lock);
+#define for_each_thermal_sensor(pos) \
+ list_for_each_entry(pos, &thermal_sensor_list, node)
+
+#define for_each_thermal_zone(pos) \
+ list_for_each_entry(pos, &thermal_zone_list, node)
+
+#define GET_INDEX(tz, ptr, type) \
+({ \
+ int i, ret = -EINVAL; \
+ do { \
+ if (!tz || !ptr) \
+ break; \
+ mutex_lock(&type##_list_lock); \
+ for (i = 0; i < tz->type##_indx; i++) { \
+ if (tz->type##s[i] == ptr) { \
+ ret = i; \
+ break; \
+ } \
+ } \
+ mutex_unlock(&type##_list_lock); \
+ } while (0); \
+ ret; \
+})
+
static struct thermal_governor *__find_governor(const char *name)
{
struct thermal_governor *pos;
@@ -419,15 +446,44 @@ static void thermal_zone_device_check(struct work_struct *work)
thermal_zone_device_update(tz);
}
+static void remove_sensor_from_zone(struct thermal_zone *tz,
+ struct thermal_sensor *ts)
+{
+ int j, indx;
+
+ indx = GET_INDEX(tz, ts, sensor);
+ if (indx < 0)
+ return;
+
+ sysfs_remove_link(&tz->device.kobj, kobject_name(&ts->device.kobj));
+
+ /* Shift the entries in the tz->sensors array */
+ for (j = indx; j < MAX_SENSORS_PER_ZONE - 1; j++)
+ tz->sensors[j] = tz->sensors[j + 1];
+
+ tz->sensor_indx--;
+}
+
/* sys I/F for thermal zone */
#define to_thermal_zone(_dev) \
container_of(_dev, struct thermal_zone_device, device)
+#define to_zone(_dev) \
+ container_of(_dev, struct thermal_zone, device)
+
#define to_thermal_sensor(_dev) \
container_of(_dev, struct thermal_sensor, device)
static ssize_t
+zone_name_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct thermal_zone *tz = to_zone(dev);
+
+ return sprintf(buf, "%s\n", tz->name);
+}
+
+static ssize_t
sensor_name_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct thermal_sensor *ts = to_thermal_sensor(dev);
@@ -809,6 +865,8 @@ static DEVICE_ATTR(policy, S_IRUGO | S_IWUSR, policy_show, policy_store);
static DEVICE_ATTR(sensor_name, 0444, sensor_name_show, NULL);
static DEVICE_ATTR(temp_input, 0444, sensor_temp_show, NULL);
+static DEVICE_ATTR(zone_name, 0444, zone_name_show, NULL);
+
/* sys I/F for cooling device */
#define to_cooling_device(_dev) \
container_of(_dev, struct thermal_cooling_device, device)
@@ -1654,6 +1712,136 @@ static int enable_sensor_thresholds(struct thermal_sensor *ts, int count)
return 0;
}
+struct thermal_zone *create_thermal_zone(const char *name, void *devdata)
+{
+ struct thermal_zone *tz;
+ int ret;
+
+ if (!name || (name && strlen(name) >= THERMAL_NAME_LENGTH))
+ return ERR_PTR(-EINVAL);
+
+ tz = kzalloc(sizeof(*tz), GFP_KERNEL);
+ if (!tz)
+ return ERR_PTR(-ENOMEM);
+
+ idr_init(&tz->idr);
+ ret = get_idr(&thermal_zone_idr, &thermal_idr_lock, &tz->id);
+ if (ret)
+ goto exit_free;
+
+ strcpy(tz->name, name);
+ tz->devdata = devdata;
+ tz->device.class = &thermal_class;
+
+ dev_set_name(&tz->device, "zone%d", tz->id);
+ ret = device_register(&tz->device);
+ if (ret)
+ goto exit_idr;
+
+ ret = device_create_file(&tz->device, &dev_attr_zone_name);
+ if (ret)
+ goto exit_unregister;
+
+ /* Add this zone to the global list of thermal zones */
+ mutex_lock(&zone_list_lock);
+ list_add_tail(&tz->node, &thermal_zone_list);
+ mutex_unlock(&zone_list_lock);
+ return tz;
+
+exit_unregister:
+ device_unregister(&tz->device);
+exit_idr:
+ release_idr(&thermal_zone_idr, &thermal_idr_lock, tz->id);
+exit_free:
+ kfree(tz);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(create_thermal_zone);
+
+void remove_thermal_zone(struct thermal_zone *tz)
+{
+ struct thermal_zone *pos, *next;
+ bool found = false;
+
+ if (!tz)
+ return;
+
+ mutex_lock(&zone_list_lock);
+
+ list_for_each_entry_safe(pos, next, &thermal_zone_list, node) {
+ if (pos == tz) {
+ list_del(&tz->node);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ goto exit;
+
+ device_remove_file(&tz->device, &dev_attr_zone_name);
+
+ release_idr(&thermal_zone_idr, &thermal_idr_lock, tz->id);
+ idr_destroy(&tz->idr);
+
+ device_unregister(&tz->device);
+ kfree(tz);
+exit:
+ mutex_unlock(&zone_list_lock);
+ return;
+}
+EXPORT_SYMBOL(remove_thermal_zone);
+
+struct thermal_sensor *get_sensor_by_name(const char *name)
+{
+ struct thermal_sensor *pos;
+ struct thermal_sensor *ts = NULL;
+
+ mutex_lock(&sensor_list_lock);
+ for_each_thermal_sensor(pos) {
+ if (!strnicmp(pos->name, name, THERMAL_NAME_LENGTH)) {
+ ts = pos;
+ break;
+ }
+ }
+ mutex_unlock(&sensor_list_lock);
+ return ts;
+}
+EXPORT_SYMBOL(get_sensor_by_name);
+
+int add_sensor_to_zone(struct thermal_zone *tz, struct thermal_sensor *ts)
+{
+ int ret;
+
+ if (!tz || !ts)
+ return -EINVAL;
+
+ mutex_lock(&zone_list_lock);
+
+ /* Ensure we are not adding the same sensor again!! */
+ ret = GET_INDEX(tz, ts, sensor);
+ if (ret >= 0) {
+ ret = -EEXIST;
+ goto exit_zone;
+ }
+
+ mutex_lock(&sensor_list_lock);
+
+ ret = sysfs_create_link(&tz->device.kobj, &ts->device.kobj,
+ kobject_name(&ts->device.kobj));
+ if (ret)
+ goto exit_sensor;
+
+ tz->sensors[tz->sensor_indx++] = ts;
+
+exit_sensor:
+ mutex_unlock(&sensor_list_lock);
+exit_zone:
+ mutex_unlock(&zone_list_lock);
+ return ret;
+}
+EXPORT_SYMBOL(add_sensor_to_zone);
+
/**
* thermal_sensor_register - register a new thermal sensor
* @name: name of the thermal sensor
@@ -1730,6 +1918,7 @@ EXPORT_SYMBOL(thermal_sensor_register);
void thermal_sensor_unregister(struct thermal_sensor *ts)
{
int i;
+ struct thermal_zone *tz;
struct thermal_sensor *pos, *next;
bool found = false;
@@ -1749,6 +1938,13 @@ void thermal_sensor_unregister(struct thermal_sensor *ts)
if (!found)
return;
+ mutex_lock(&zone_list_lock);
+
+ for_each_thermal_zone(tz)
+ remove_sensor_from_zone(tz, ts);
+
+ mutex_unlock(&zone_list_lock);
+
for (i = 0; i < ts->thresholds; i++) {
device_remove_file(&ts->device, &ts->thresh_attrs[i].attr);
if (ts->ops->get_hyst) {
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index a49cb38..f5b9540 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -49,6 +49,8 @@
/* Default Thermal Governor: Does Linear Throttling */
#define DEFAULT_THERMAL_GOVERNOR "step_wise"
+#define MAX_SENSORS_PER_ZONE 5
+
struct thermal_sensor;
struct thermal_zone_device;
struct thermal_cooling_device;
@@ -194,6 +196,21 @@ struct thermal_zone_device {
struct delayed_work poll_queue;
};
+struct thermal_zone {
+ char name[THERMAL_NAME_LENGTH];
+ int id;
+ void *devdata;
+ struct thermal_zone *ops;
+ struct thermal_governor *governor;
+ struct idr idr;
+ struct device device;
+ struct list_head node;
+
+ /* Sensor level information */
+ int sensor_indx; /* index into 'sensors' array */
+ struct thermal_sensor *sensors[MAX_SENSORS_PER_ZONE];
+};
+
/* Structure that holds thermal governor information */
struct thermal_governor {
char name[THERMAL_NAME_LENGTH];
@@ -266,6 +283,11 @@ struct thermal_sensor *thermal_sensor_register(const char *, int,
struct thermal_sensor_ops *, void *);
void thermal_sensor_unregister(struct thermal_sensor *);
+struct thermal_zone *create_thermal_zone(const char *, void *);
+void remove_thermal_zone(struct thermal_zone *);
+int add_sensor_to_zone(struct thermal_zone *, struct thermal_sensor *);
+struct thermal_sensor *get_sensor_by_name(const char *);
+
#ifdef CONFIG_NET
extern int thermal_generate_netlink_event(u32 orig, enum events event);
#else
--
1.7.9.5
^ permalink raw reply related
* [PATCHv2 0/9] Thermal Framework Enhancements
From: Durgadoss R @ 2013-01-07 7:13 UTC (permalink / raw)
To: rui.zhang, linux-pm
Cc: linux-kernel, eduardo.valentin, hongbo.zhang, wni, Durgadoss R
This patch set is a v2 of the previous versions submitted here:
[v1]: https://lkml.org/lkml/2012/12/18/108
[RFC]: https://patchwork.kernel.org/patch/1758921/
This patch set is based on Rui's -thermal tree, and is
tested on a Core-i5 and an Atom netbook.
Changes Since v1:
* Removed kobject creation for thermal_trip and thermal_map
nodes as per Greg-KH's comments.
* Added ABI Documentation under 'testing'.
* Modified the GET_INDEX macro to be more linux-like, thanks
to Joe Perches.
* Added get_[sensor/cdev]_by_name APIs to thermal.h
This series contains 9 patches:
Patch 1/9: Creates new sensor level APIs
Patch 2/9: Creates new zone level APIs. The existing tzd structure is
kept as such for clarity and compatibility purposes.
Patch 3/9: Creates functions to add/remove a cdev to/from a zone. The
existing tcd structure need not be modified.
Patch 4/9: Adds sensorX_trip_[active/passive/hot/critical] sysfs nodes,
under /sys/class/thermal/zoneY/. This exposes various trip
points for sensorX present in zoneY.
Patch 5/9: Adds mapX sysfs node. It is a compact representation
of the binding relationship between a sensor and a cdev,
within a zone.
Patch 6/9: Creates Documentation for the new APIs. A new file is
created for clarity. Final goal is to merge with the existing
file or refactor the files, as whatever seems appropriate.
Patch 7/9: Make PER ZONE values configurable through Kconfig
Patch 8/9: Add ABI documentation for sysfs interfaces introduced in this patch.
Patch 9/9: A dummy driver that can be used for testing. This is not for merge.
Thanks to Greg-KH, Hongbo Zhang and Joe Perches for their comments on v1.
Durgadoss R (9):
Thermal: Create sensor level APIs
Thermal: Create zone level APIs
Thermal: Add APIs to bind cdev to new zone structure
Thermal: Add trip point sysfs nodes for sensor
Thermal: Create 'mapX' sysfs node for a zone
Thermal: Add Documentation to new APIs
Thermal: Make PER_ZONE values configurable
Thermal: Add ABI Documentation for sysfs interfaces
Thermal: Dummy driver used for testing
Documentation/ABI/testing/sysfs-class-thermal | 93 +++
Documentation/thermal/sysfs-api2.txt | 248 +++++++
drivers/thermal/Kconfig | 19 +
drivers/thermal/Makefile | 3 +
drivers/thermal/thermal_sys.c | 881 +++++++++++++++++++++++++
drivers/thermal/thermal_test.c | 315 +++++++++
include/linux/thermal.h | 117 +++-
7 files changed, 1675 insertions(+), 1 deletion(-)
create mode 100644 Documentation/ABI/testing/sysfs-class-thermal
create mode 100644 Documentation/thermal/sysfs-api2.txt
create mode 100644 drivers/thermal/thermal_test.c
--
1.7.9.5
^ permalink raw reply
* [PATCH 9/9] Thermal: Dummy driver used for testing
From: Durgadoss R @ 2013-01-07 7:13 UTC (permalink / raw)
To: rui.zhang, linux-pm
Cc: linux-kernel, eduardo.valentin, hongbo.zhang, wni, Durgadoss R
In-Reply-To: <1357542806-20449-1-git-send-email-durgadoss.r@intel.com>
This patch has a dummy driver that can be used for
testing purposes. This patch is not for merge.
Signed-off-by: Durgadoss R <durgadoss.r@intel.com>
---
drivers/thermal/Kconfig | 5 +
drivers/thermal/Makefile | 3 +
drivers/thermal/thermal_test.c | 329 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 337 insertions(+)
create mode 100644 drivers/thermal/thermal_test.c
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index c5ba3340..3b92a76 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -136,4 +136,9 @@ config DB8500_CPUFREQ_COOLING
bound cpufreq cooling device turns active to set CPU frequency low to
cool down the CPU.
+config THERMAL_TEST
+ tristate "test driver"
+ help
+ Enable this to test the thermal framework.
+
endif
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index d8da683..02c3edb 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -18,3 +18,6 @@ obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o
obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o
obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
+
+# dummy driver for testing
+obj-$(CONFIG_THERMAL_TEST) += thermal_test.o
diff --git a/drivers/thermal/thermal_test.c b/drivers/thermal/thermal_test.c
new file mode 100644
index 0000000..137b562
--- /dev/null
+++ b/drivers/thermal/thermal_test.c
@@ -0,0 +1,329 @@
+/*
+ * thermal_test.c - This driver can be used to test Thermal
+ * Framework changes. Not specific to any
+ * platform. Fills the log buffer generously ;)
+ *
+ * Copyright (C) 2012 Intel Corporation
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Author: Durgadoss R <durgadoss.r@intel.com>
+ */
+
+#define pr_fmt(fmt) "thermal_test: " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/param.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/thermal.h>
+
+#define MAX_THERMAL_ZONES 2
+#define MAX_THERMAL_SENSORS 2
+#define MAX_COOLING_DEVS 4
+#define NUM_THRESHOLDS 3
+
+static struct ts_data {
+ int curr_temp;
+ int flag;
+} ts_data;
+
+int active_trips[10] = {100, 90, 80, 70, 60, 50, 40, 30, 20, 10};
+int passive_trips[5] = {100, 90, 60, 50, 40};
+int wts[5] = {50, 50, 50, 50, 40};
+
+static struct platform_device *pdev;
+static unsigned long cur_cdev_state = 2;
+static struct thermal_sensor *ts, *ts1;
+static struct thermal_zone *tz;
+static struct thermal_cooling_device *cdev;
+
+static long thermal_thresholds[NUM_THRESHOLDS] = {30000, 40000, 50000};
+
+static struct thermal_trip_point trip = {
+ .hot = 90,
+ .crit = 100,
+ .num_passive_trips = 5,
+ .passive_trips = passive_trips,
+ .num_active_trips = 10,
+ .active_trips = active_trips,
+ .active_trip_mask = 0xCFF,
+};
+
+static struct thermal_trip_point trip1 = {
+ .hot = 95,
+ .crit = 125,
+ .num_passive_trips = 0,
+ .passive_trips = passive_trips,
+ .num_active_trips = 6,
+ .active_trips = active_trips,
+ .active_trip_mask = 0xFF,
+};
+
+static struct thermal_map map = {
+ .trip_type = THERMAL_TRIP_PASSIVE,
+ .sensor_name = "ts",
+ .cdev_name = "cdev",
+ .num_weights = 5,
+ .trip_mask = 0x0F,
+ .weights = wts,
+};
+
+static int read_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ *state = cur_cdev_state;
+ return 0;
+}
+
+static int write_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long state)
+{
+ cur_cdev_state = state;
+ return 0;
+}
+
+static int read_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ *state = 5;
+ return 0;
+}
+
+static int read_curr_temp(struct thermal_sensor *ts, long *temp)
+{
+ *temp = ts_data.curr_temp;
+ return 0;
+}
+
+static ssize_t
+flag_show(struct device *dev, struct device_attribute *devattr, char *buf)
+{
+ return sprintf(buf, "%d\n", ts_data.flag);
+}
+
+static ssize_t
+flag_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ long flag;
+
+ if (kstrtol(buf, 10, &flag))
+ return -EINVAL;
+
+ ts_data.flag = flag;
+
+ if (flag == 0) {
+ thermal_sensor_unregister(ts);
+ ts = NULL;
+ pr_err("thermal_sensor_unregister (ts) done\n");
+ } else if (flag == 1) {
+ thermal_sensor_unregister(ts1);
+ ts1 = NULL;
+ pr_err("thermal_sensor_unregister (ts1) done\n");
+ } else if (flag == 2) {
+ thermal_cooling_device_unregister(cdev);
+ cdev = NULL;
+ pr_err("cdev unregister (cdev) done\n");
+ } else if (flag == 3) {
+ if (tz)
+ remove_thermal_zone(tz);
+ tz = NULL;
+ pr_err("removed thermal zone\n");
+ }
+
+ return count;
+}
+
+static ssize_t
+temp_show(struct device *dev, struct device_attribute *devattr, char *buf)
+{
+ return sprintf(buf, "%d\n", ts_data.curr_temp);
+}
+
+static int read_threshold(struct thermal_sensor *ts, int indx, long *val)
+{
+ if (indx < 0 || indx >= NUM_THRESHOLDS)
+ return -EINVAL;
+
+ *val = thermal_thresholds[indx];
+ return 0;
+}
+
+static int write_threshold(struct thermal_sensor *ts, int indx, long val)
+{
+ if (indx < 0 || indx >= NUM_THRESHOLDS)
+ return -EINVAL;
+
+ thermal_thresholds[indx] = val;
+ return 0;
+}
+
+static ssize_t
+temp_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ long temp;
+
+ if (kstrtol(buf, 10, &temp))
+ return -EINVAL;
+
+ ts_data.curr_temp = temp;
+ return count;
+}
+
+static struct thermal_sensor_ops ts_ops = {
+ .get_temp = read_curr_temp,
+ .get_threshold = read_threshold,
+ .set_threshold = write_threshold,
+};
+
+static struct thermal_sensor_ops ts1_ops = {
+ .get_temp = read_curr_temp,
+ .get_threshold = read_threshold,
+ .set_threshold = write_threshold,
+};
+
+static struct thermal_cooling_device_ops cdev_ops = {
+ .get_cur_state = read_cur_state,
+ .set_cur_state = write_cur_state,
+ .get_max_state = read_max_state,
+};
+
+static DEVICE_ATTR(test_temp, S_IRUGO | S_IWUSR, temp_show, temp_store);
+static DEVICE_ATTR(sensor_enable, S_IRUGO | S_IWUSR, flag_show, flag_store);
+
+static int thermal_test_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ts_data.curr_temp = 30000;
+ ts_data.flag = 1;
+
+ ts = thermal_sensor_register("ts", NUM_THRESHOLDS, &ts_ops, &ts_data);
+ if (!ts) {
+ pr_err("thermal_sensor_register failed:\n");
+ return -EINVAL;
+ }
+
+ ts1 = thermal_sensor_register("ts1", NUM_THRESHOLDS, &ts1_ops, NULL);
+
+ cdev = thermal_cooling_device_register("cdev", NULL, &cdev_ops);
+ if (!cdev) {
+ pr_err("cdev_register failed:\n");
+ return -EINVAL;
+ }
+
+ device_create_file(&pdev->dev, &dev_attr_test_temp);
+ device_create_file(&pdev->dev, &dev_attr_sensor_enable);
+
+ /* Create a zone */
+ tz = create_thermal_zone("myZone", NULL);
+ if (!tz) {
+ pr_err("create_thermal_zone failed:\n");
+ return -EINVAL;
+ }
+
+ pr_err("Zone created successfully..\n");
+
+ ret = add_sensor_to_zone(tz, ts);
+ if (ret) {
+ pr_err("add_sensor_to_zone failed:%d\n", ret);
+ return ret;
+ }
+
+ ret = add_sensor_to_zone(tz, ts1);
+ pr_err("add_sensor (ts1) ret_val: %d\n", ret);
+
+ ret = add_cdev_to_zone(tz, cdev);
+ pr_err("add_cdev_to_zone (cdev) ret_val: %d\n", ret);
+
+ ret = add_sensor_trip_info(tz, ts, &trip);
+ ret = add_sensor_trip_info(tz, ts1, &trip1);
+ pr_err("add_sensor_trip_info (ts) ret_val: %d\n", ret);
+
+ ret = add_map_entry(tz, &map);
+ pr_err("add_map_entry (ts) ret_val: %d\n", ret);
+
+ return 0;
+}
+
+static int thermal_test_remove(struct platform_device *pdev)
+{
+ device_remove_file(&pdev->dev, &dev_attr_test_temp);
+ device_remove_file(&pdev->dev, &dev_attr_sensor_enable);
+
+ return 0;
+}
+
+/*********************************************************************
+ * Driver initialization and finalization
+ *********************************************************************/
+
+#define DRIVER_NAME "thermal_test"
+
+static const struct platform_device_id therm_id_table[] = {
+ { DRIVER_NAME, 1 },
+};
+
+static struct platform_driver thermal_test_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = thermal_test_probe,
+ .remove = __devexit_p(thermal_test_remove),
+ .id_table = therm_id_table,
+};
+
+static int __init thermal_test_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&thermal_test_driver);
+ if (ret) {
+ pr_err("platform driver register failed:%d\n", ret);
+ return ret;
+ }
+
+ pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0);
+ if (IS_ERR(pdev)) {
+ ret = PTR_ERR(pdev);
+ pr_err("platform device register failed:%d\n", ret);
+ platform_driver_unregister(&thermal_test_driver);
+ }
+
+ return ret;
+}
+
+static void __exit thermal_test_exit(void)
+{
+ pr_err("in thermal_test_exit\n");
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&thermal_test_driver);
+}
+
+module_init(thermal_test_init);
+module_exit(thermal_test_exit);
+
+MODULE_AUTHOR("Durgadoss R <durgadoss.r@intel.com>");
+MODULE_DESCRIPTION("A dummy driver to test Thermal Framework");
+MODULE_LICENSE("GPL");
--
1.7.9.5
^ permalink raw reply related
* [PATCH 7/9] Thermal: Make PER_ZONE values configurable
From: Durgadoss R @ 2013-01-07 7:13 UTC (permalink / raw)
To: rui.zhang, linux-pm
Cc: linux-kernel, eduardo.valentin, hongbo.zhang, wni, Durgadoss R
In-Reply-To: <1357542806-20449-1-git-send-email-durgadoss.r@intel.com>
This patch makes MAX_SENSORS_PER_ZONE and
MAX_CDEVS_PER_ZONE values configurable. The
default value is 1, and range is 1-12.
Signed-off-by: Durgadoss R <durgadoss.r@intel.com>
---
drivers/thermal/Kconfig | 14 ++++++++++++++
include/linux/thermal.h | 6 +++---
2 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index d96da07..c5ba3340 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -15,6 +15,20 @@ menuconfig THERMAL
if THERMAL
+config THERMAL_MAX_SENSORS_PER_ZONE
+ int "Maximum number of sensors allowed per thermal zone"
+ default 1
+ range 1 12
+ ---help---
+ Specify the number of sensors allowed per zone
+
+config THERMAL_MAX_CDEVS_PER_ZONE
+ int "Maximum number of cooling devices allowed per thermal zone"
+ default 1
+ range 1 12
+ ---help---
+ Specify the number of cooling devices allowed per zone
+
config THERMAL_HWMON
bool
depends on HWMON=y || HWMON=THERMAL
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 187fadb..cf19fba 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -50,9 +50,9 @@
/* Default Thermal Governor: Does Linear Throttling */
#define DEFAULT_THERMAL_GOVERNOR "step_wise"
-#define MAX_SENSORS_PER_ZONE 5
-
-#define MAX_CDEVS_PER_ZONE 5
+/* Maximum number of sensors/cdevs per zone, defined through Kconfig */
+#define MAX_SENSORS_PER_ZONE CONFIG_THERMAL_MAX_SENSORS_PER_ZONE
+#define MAX_CDEVS_PER_ZONE CONFIG_THERMAL_MAX_CDEVS_PER_ZONE
/* If we map each sensor with every possible cdev for a zone */
#define MAX_MAPS_PER_ZONE (MAX_SENSORS_PER_ZONE * MAX_CDEVS_PER_ZONE)
--
1.7.9.5
^ permalink raw reply related
* [PATCH 6/9] Thermal: Add Documentation to new APIs
From: Durgadoss R @ 2013-01-07 7:13 UTC (permalink / raw)
To: rui.zhang, linux-pm
Cc: linux-kernel, eduardo.valentin, hongbo.zhang, wni, Durgadoss R
In-Reply-To: <1357542806-20449-1-git-send-email-durgadoss.r@intel.com>
This patch adds Documentation for the new APIs
introduced in this patch set. The documentation
also has a model sysfs structure for reference.
Signed-off-by: Durgadoss R <durgadoss.r@intel.com>
---
Documentation/thermal/sysfs-api2.txt | 248 ++++++++++++++++++++++++++++++++++
1 file changed, 248 insertions(+)
create mode 100644 Documentation/thermal/sysfs-api2.txt
diff --git a/Documentation/thermal/sysfs-api2.txt b/Documentation/thermal/sysfs-api2.txt
new file mode 100644
index 0000000..ffd0402
--- /dev/null
+++ b/Documentation/thermal/sysfs-api2.txt
@@ -0,0 +1,248 @@
+Thermal Framework
+-----------------
+
+Written by Durgadoss R <durgadoss.r@intel.com>
+Copyright (c) 2012 Intel Corporation
+
+Created on: 4 November 2012
+Updated on: 18 December 2012
+
+0. Introduction
+---------------
+The Linux thermal framework provides a set of interfaces for thermal
+sensors and thermal cooling devices (fan, processor...) to register
+with the thermal management solution and to be a part of it.
+
+This document focuses on how to enable new thermal sensors and cooling
+devices to participate in thermal management. This solution is intended
+to be 'light-weight' and platform/architecture independent. Any thermal
+sensor/cooling device should be able to use the infrastructure easily.
+
+The goal of thermal framework is to expose the thermal sensor/zone and
+cooling device attributes in a consistent way. This will help the
+thermal governors to make use of the information to manage platform
+thermals efficiently.
+
+The thermal sensor source file can be generic (can be any sensor driver,
+in any subsystem). This driver will use the sensor APIs and register with
+thermal framework to participate in platform Thermal management. This
+does not (and should not) know about which zone it belongs to, or any
+other information about platform thermals. A sensor driver is a standalone
+piece of code, which can optionally register with thermal framework.
+
+However, for any platform, there should be a platformX_thermal.c file,
+which will know about the platform thermal characteristics (like how many
+sensors, zones, cooling devices, etc.. And how they are related to each other
+i.e the mapping information). Only in this file, the zone level APIs should
+be used, in which case the file will have all information required to attach
+various sensors to a particular zone.
+
+This way, we can have one platform level thermal file, which can support
+multiple platforms (may be)using the same set of sensors (but)binded in
+a different way. This file can get the platform thermal information
+through Firmware, ACPI tables, device tree etc.
+
+Unfortunately, today we don't have many drivers that can be clearly
+differentiated as 'sensor_file.c' and 'platform_thermal_file.c'.
+But very soon we will need/have. The reason I am saying this is because
+we are seeing a lot of chip drivers, starting to use thermal framework,
+and we should keep it really light-weight for them to do so.
+
+An Example: drivers/hwmon/emc1403.c - a generic thermal chip driver
+In one platform this sensor can belong to 'ZoneA' and in another the
+same can belong to 'ZoneB'. But, emc1403.c does not really care about
+where does it belong. It just reports temperature.
+
+1. Terminology
+--------------
+This section describes the terminology used in the rest of this
+document as well as the thermal framework code.
+
+thermal_sensor: Hardware that can report temperature of a particular
+ spot in the platform, where it is placed. The temperature
+ reported by the sensor is the 'real' temperature reported
+ by the hardware.
+thermal_zone: A virtual area on the device, that gets heated up. It may
+ have one or more thermal sensors attached to it.
+cooling_device: Any component that can help in reducing the temperature of
+ a 'hot spot' either by reducing its performance (passive
+ cooling) or by other means(Active cooling E.g. Fan)
+
+trip_points: Various temperature levels for each sensor. As of now, we
+ have four levels namely active, passive, hot and critical.
+ Hot and critical trip point support only one value whereas
+ active and passive can have any number of values. These
+ temperature values can come from platform data, and are
+ exposed through sysfs in a consistent manner. Stand-alone
+ thermal sensor drivers are not expected to know these values.
+ These values are RO.
+thresholds: These are programmable temperature limits, on reaching which
+ the thermal sensor generates an interrupt. The framework is
+ notified about this interrupt to take appropriate action.
+ There can be as many number of thresholds as that of the
+ hardware supports. These values are RW.
+
+thermal_map: This provides the mapping (aka binding) information between
+ various sensors and cooling devices in a particular zone.
+ Typically, this also comes from platform data; Stand-alone
+ sensor drivers or cooling device drivers are not expected
+ to know these mapping information.
+
+2. Thermal framework APIs
+-------------------------
+2.1: For Thermal Sensors
+2.1.1 thermal_sensor_register:
+ This function creates a new sensor directory under /sys/class/thermal/
+ as sensor[0-*]. This API is expected to be called by thermal sensor
+ drivers. These drivers may or may not be in thermal subsystem. This
+ function returns a thermal_sensor structure on success and appropriate
+ error on failure.
+
+ name: Name of the sensor
+ count: Number of programmable thresholds associated with this sensor
+ devdata: Device private data
+ ops: Thermal sensor callbacks
+ .get_temp: obtain the current temperature of the sensor
+ .get_trend: obtain the trend of the sensor
+ .get_threshold: get a particular threshold temperature
+ .set_threshold: set a particular threshold temperature
+ .get_hyst: get hysteresis value associated with a threshold
+ .set_hyst: set hysteresis value associated with a threshold
+
+2.1.2 thermal_sensor_unregister:
+ This function deletes the sensor directory under /sys/class/thermal/
+ for the given sensor. Thermal sensor drivers may call this API
+ during the driver's 'exit' routine.
+
+ ts: Thermal sensor that has to be unregistered
+
+2.1.3 enable_sensor_thresholds:
+ This function creates 'threshold[0-*]' attributes under a particular
+ sensorX directory. These values are RW. This function is called by
+ the sensr driver only if the sensor supports interrupt mechanism.
+
+ ts: Thermal sensor for which thresholds have to be enabled
+ num_thresholds: Number of thresholds supported by the sensor
+
+2.2: For Cooling Devices
+2.2.1 thermal_cooling_device_register:
+ This function adds a new thermal cooling device (fan/processor/...)
+ to /sys/class/thermal/ folder as cooling_device[0-*]. This function
+ is expected to be called by cooling device drivers that may be
+ present in other subsystems also.
+
+ name: the cooling device name
+ devdata: device private data
+ ops: thermal cooling devices callbacks
+ .get_max_state: get the Maximum throttle state of the cooling device
+ .get_cur_state: get the Current throttle state of the cooling device
+ .set_cur_state: set the Current throttle state of the cooling device
+
+2.2.2 thermal_cooling_device_unregister:
+ This function deletes the given cdev entry form /sys/class/thermal;
+ and also cleans all the symlinks referred from various zones.
+
+ cdev: Cooling device to be unregistered
+
+2.3: For Thermal Zones
+2.3.1 create_thermal_zone:
+ This function adds a new 'zone' under /sys/class/thermal/
+ directory as zone[0-*]. This zone has at least one thermal
+ sensor and at most MAX_SENSORS_PER_ZONE number of sensors
+ attached to it. Similarly, this zone has at least one cdev
+ and at most MAX_CDEVS_PER_ZONE number of cdevs attached to it.
+ Both the MAX_*_PER_ZONE values are configurable, through
+ Kconfig option(during 'menuconfig').
+
+ name: Name of the thermal zone
+ devdata: Device private data
+
+2.3.2 add_sensor_to_zone
+ This function adds a 'sensorX' entry under /sys/class/thermal/
+ zoneY/ directory. This 'sensorX' is a symlink to the actual
+ sensor entry under /sys/class/thermal/. Correspondingly, the
+ method remove_sensor_from_zone deletes the symlink.
+
+ tz: thermal zone structure
+ ts: thermal sensor structure
+
+2.3.3 add_cdev_to_zone
+ This function adds a 'cdevX' entry under /sys/class/thermal/
+ zoneY/ directory. This 'cdevX' is a symlink to the actual
+ cdev entry under /sys/class/thermal/. Correspondingly, the
+ method remove_cdev_from_zone deletes the symlink.
+
+ tz: thermal zone structure
+ cdev: thermal cooling device structure
+
+2.4 For Thermal Trip
+2.4.1 add_sensor_trip_info
+ This function adds trip point information for the given sensor,
+ (under a given zone) under /sys/class/thermal/zoneX/thermal_trip/
+ This API creates 4 sysfs attributes namely active, passive, hot,
+ and critical. Each of these hold one or more trip point temperature
+ values, as provided from platform data.
+
+ tz: thermal zone structure
+ ts: thermal sensor to which the trip points are attached
+ trip: trip point structure. Usually obtained from platform data
+
+2.5 For Thermal Map
+2.5.1 add_map_entry
+ This function adds a 'map[0-*]' sysfs attribute under
+ /sys/class/thermal/zoneX/thermal_map/. Each map holds a space
+ separated list of values, that specify the binding relationship
+ between a sensor and a cdev in the given zone. The map structure
+ is typically obtained as platform data. For example, through
+ ACPI tables, SFI tables, Device tree etc.
+
+ tz: thermal zone to which a 'map' is being added
+ map: thermal_map structure
+
+3. Sysfs attributes structure
+-----------------------------
+Thermal sysfs attributes will be represented under /sys/class/thermal.
+
+3.1: For Thermal Sensors
+ /sys/class/thermal/sensor[0-*]:
+ |---type: Name of the thermal sensor
+ |---temp_input: Current temperature in mC
+ |---threshold[0-*]: Threshold temperature in mC
+ |---threshold[0-*]_hyst:Optional hysteresis value in mC
+
+3.2: For Thermal Cooling Devices
+ /sys/class/thermal/cooling_device[0-*]:
+ |---type: Type of the cooling device
+ |---max_state: Maximum throttle state of the cdev
+ |---cur_state: Current throttle state of the cdev
+
+3.3: For Thermal Zones
+ /sys/class/thermal/zone[0-*]:
+ |---name: Name of the thermal
+ |---sensorX: Symlink to ../sensorX
+ |---cdevY: Symlink to ../cdevY
+ |---thermal_trip: trip point values for sensors
+ |---thermal_map: mapping info between sensors and cdevs
+
+3.4: For Thermal Trip
+ This attribute represents the trip point values for all sensors
+ present in the thermal zone. All values are in mC.
+ /sys/class/thermal/zoneX/thermal_trip/sensorY:
+ |---hot: hot trip point value
+ |---critical: critical trip point value
+ |---passive: list of passive trip point values
+ |---active: list of active trip point values
+
+3.5: For Thermal Map
+ Each attribute represents the mapping/binding information between
+ a sensor and a cdev, together with a trip type.
+ /sys/class/thermal/zoneX/thermal_map/:
+ |---mapX: mapping information
+ A typical map entry is like below:
+
+ trip_type sensor cdev trip_mask weight(s)
+ passive cpu proc 0x03 50 30
+ active cpu fan0 0x03 50 70
+
+ The trip mask is a bit string; if 'n' th bit is set, then for
+ trip point 'n' this cdev is throttled with the given weight[n].
--
1.7.9.5
^ permalink raw reply related
* [PATCH 5/9] Thermal: Create 'mapX' sysfs node for a zone
From: Durgadoss R @ 2013-01-07 7:13 UTC (permalink / raw)
To: rui.zhang, linux-pm
Cc: linux-kernel, eduardo.valentin, hongbo.zhang, wni, Durgadoss R
In-Reply-To: <1357542806-20449-1-git-send-email-durgadoss.r@intel.com>
This patch creates a thermal map sysfs node under
/sys/class/thermal/zoneX/. This contains
entries named map0, map1 .. mapN. Each map has the
following space separated values:
trip_type sensor_name cdev_name trip_mask weights
Signed-off-by: Durgadoss R <durgadoss.r@intel.com>
---
drivers/thermal/thermal_sys.c | 122 ++++++++++++++++++++++++++++++++++++++++-
include/linux/thermal.h | 19 +++++++
2 files changed, 139 insertions(+), 2 deletions(-)
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index 1958bb8..5f806d1 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -509,6 +509,39 @@ static void remove_cdev_from_zone(struct thermal_zone *tz,
tz->cdev_indx--;
}
+static inline void __clean_map_entry(struct thermal_zone *tz, int i)
+{
+ device_remove_file(&tz->device, &tz->map_attr[i]->attr);
+ kfree(tz->map_attr[i]);
+ tz->map[i] = NULL;
+}
+
+static void remove_sensor_map_entry(struct thermal_zone *tz,
+ struct thermal_sensor *ts)
+{
+ int i;
+
+ for (i = 0; i < MAX_MAPS_PER_ZONE; i++) {
+ if (tz->map[i] && !strnicmp(ts->name, tz->map[i]->sensor_name,
+ THERMAL_NAME_LENGTH)) {
+ __clean_map_entry(tz, i);
+ }
+ }
+}
+
+static void remove_cdev_map_entry(struct thermal_zone *tz,
+ struct thermal_cooling_device *cdev)
+{
+ int i;
+
+ for (i = 0; i < MAX_MAPS_PER_ZONE; i++) {
+ if (tz->map[i] && !strnicmp(cdev->type, tz->map[i]->cdev_name,
+ THERMAL_NAME_LENGTH)) {
+ __clean_map_entry(tz, i);
+ }
+ }
+}
+
/* sys I/F for thermal zone */
#define to_thermal_zone(_dev) \
@@ -901,6 +934,39 @@ policy_show(struct device *dev, struct device_attribute *devattr, char *buf)
}
static ssize_t
+map_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ char *trip;
+ int i, indx, ret = 0;
+ struct thermal_map *map;
+ struct thermal_zone *tz = to_zone(dev);
+
+ if (!sscanf(attr->attr.name, "map%d", &indx))
+ return -EINVAL;
+
+ if (indx < 0 || indx >= MAX_MAPS_PER_ZONE)
+ return -EINVAL;
+
+ if (!tz->map[indx])
+ return sprintf(buf, "<Unavailable>\n");
+
+ map = tz->map[indx];
+
+ trip = (map->trip_type == THERMAL_TRIP_ACTIVE) ?
+ "active" : "passive";
+ ret += sprintf(buf, "%s", trip);
+ ret += sprintf(buf + ret, " %s", map->sensor_name);
+ ret += sprintf(buf + ret, " %s", map->cdev_name);
+ ret += sprintf(buf + ret, " 0x%x", map->trip_mask);
+
+ for (i = 0; i < map->num_weights; i++)
+ ret += sprintf(buf + ret, " %d", map->weights[i]);
+
+ ret += sprintf(buf + ret, "\n");
+ return ret;
+}
+
+static ssize_t
active_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int i, indx, ret = 0;
@@ -1639,8 +1705,10 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
mutex_lock(&zone_list_lock);
- for_each_thermal_zone(tmp_tz)
+ for_each_thermal_zone(tmp_tz) {
remove_cdev_from_zone(tmp_tz, cdev);
+ remove_cdev_map_entry(tmp_tz, cdev);
+ }
mutex_unlock(&zone_list_lock);
@@ -1978,6 +2046,9 @@ void remove_thermal_zone(struct thermal_zone *tz)
kobject_name(&tz->cdevs[i]->device.kobj));
}
+ for (i = 0; i < MAX_MAPS_PER_ZONE; i++)
+ __clean_map_entry(tz, i);
+
release_idr(&thermal_zone_idr, &thermal_idr_lock, tz->id);
idr_destroy(&tz->idr);
@@ -2023,6 +2094,51 @@ struct thermal_sensor *get_sensor_by_name(const char *name)
}
EXPORT_SYMBOL(get_sensor_by_name);
+int add_map_entry(struct thermal_zone *tz, struct thermal_map *map)
+{
+ int ret, indx;
+
+ if (!tz || !map)
+ return -EINVAL;
+
+ mutex_lock(&zone_list_lock);
+
+ for (indx = 0; indx < MAX_MAPS_PER_ZONE; indx++) {
+ if (tz->map[indx] == NULL)
+ break;
+ }
+
+ if (indx >= MAX_MAPS_PER_ZONE) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ tz->map_attr[indx] = kzalloc(sizeof(struct thermal_attr), GFP_KERNEL);
+ if (!tz->map_attr[indx]) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ sprintf(tz->map_attr[indx]->name, "map%d", indx);
+
+ tz->map_attr[indx]->attr.attr.name = tz->map_attr[indx]->name;
+ tz->map_attr[indx]->attr.attr.mode = S_IRUGO;
+ tz->map_attr[indx]->attr.show = map_show;
+
+ sysfs_attr_init(&tz->map_attr[indx]->attr.attr);
+ ret = device_create_file(&tz->device, &tz->map_attr[indx]->attr);
+ if (ret) {
+ kfree(tz->map_attr[indx]);
+ goto exit;
+ }
+
+ tz->map[indx] = map;
+exit:
+ mutex_unlock(&zone_list_lock);
+ return ret;
+}
+EXPORT_SYMBOL(add_map_entry);
+
int add_sensor_to_zone(struct thermal_zone *tz, struct thermal_sensor *ts)
{
int ret;
@@ -2227,8 +2343,10 @@ void thermal_sensor_unregister(struct thermal_sensor *ts)
mutex_lock(&zone_list_lock);
- for_each_thermal_zone(tz)
+ for_each_thermal_zone(tz) {
remove_sensor_from_zone(tz, ts);
+ remove_sensor_map_entry(tz, ts);
+ }
mutex_unlock(&zone_list_lock);
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 474547d..187fadb 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -54,6 +54,9 @@
#define MAX_CDEVS_PER_ZONE 5
+/* If we map each sensor with every possible cdev for a zone */
+#define MAX_MAPS_PER_ZONE (MAX_SENSORS_PER_ZONE * MAX_CDEVS_PER_ZONE)
+
struct thermal_sensor;
struct thermal_zone_device;
struct thermal_cooling_device;
@@ -159,6 +162,16 @@ struct thermal_attr {
char name[THERMAL_NAME_LENGTH];
};
+struct thermal_map {
+ enum thermal_trip_type trip_type;
+ char cdev_name[THERMAL_NAME_LENGTH];
+ char sensor_name[THERMAL_NAME_LENGTH];
+
+ int trip_mask;
+ int num_weights;
+ int *weights;
+};
+
/*
* This structure defines the trip points for a sensor.
* The actual values for these trip points come from
@@ -248,6 +261,10 @@ struct thermal_zone {
/* Thermal sensors trip information */
struct thermal_trip_point *sensor_trip[MAX_SENSORS_PER_ZONE];
struct thermal_trip_attr *trip_attr[MAX_SENSORS_PER_ZONE];
+
+ /* Thermal map information */
+ struct thermal_map *map[MAX_MAPS_PER_ZONE];
+ struct thermal_attr *map_attr[MAX_MAPS_PER_ZONE];
};
/* Structure that holds thermal governor information */
@@ -333,6 +350,8 @@ struct thermal_cooling_device *get_cdev_by_name(const char *);
int add_sensor_trip_info(struct thermal_zone *, struct thermal_sensor *,
struct thermal_trip_point *);
+int add_map_entry(struct thermal_zone *, struct thermal_map *);
+
#ifdef CONFIG_NET
extern int thermal_generate_netlink_event(u32 orig, enum events event);
#else
--
1.7.9.5
^ permalink raw reply related
* [PATCH 1/9] Thermal: Create sensor level APIs
From: Durgadoss R @ 2013-01-07 7:13 UTC (permalink / raw)
To: rui.zhang, linux-pm
Cc: linux-kernel, eduardo.valentin, hongbo.zhang, wni, Durgadoss R
In-Reply-To: <1357542806-20449-1-git-send-email-durgadoss.r@intel.com>
This patch creates sensor level APIs, in the
generic thermal framework.
A Thermal sensor is a piece of hardware that can report
temperature of the spot in which it is placed. A thermal
sensor driver reads the temperature from this sensor
and reports it out. This kind of driver can be in
any subsystem. If the sensor needs to participate
in platform thermal management, the corresponding
driver can use the APIs introduced in this patch, to
register(or unregister) with the thermal framework.
Signed-off-by: Durgadoss R <durgadoss.r@intel.com>
---
drivers/thermal/thermal_sys.c | 280 +++++++++++++++++++++++++++++++++++++++++
include/linux/thermal.h | 29 +++++
2 files changed, 309 insertions(+)
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index 8f0f37b..b2becb9 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -45,13 +45,16 @@ MODULE_LICENSE("GPL");
static DEFINE_IDR(thermal_tz_idr);
static DEFINE_IDR(thermal_cdev_idr);
+static DEFINE_IDR(thermal_sensor_idr);
static DEFINE_MUTEX(thermal_idr_lock);
static LIST_HEAD(thermal_tz_list);
+static LIST_HEAD(thermal_sensor_list);
static LIST_HEAD(thermal_cdev_list);
static LIST_HEAD(thermal_governor_list);
static DEFINE_MUTEX(thermal_list_lock);
+static DEFINE_MUTEX(sensor_list_lock);
static DEFINE_MUTEX(thermal_governor_lock);
static struct thermal_governor *__find_governor(const char *name)
@@ -421,6 +424,103 @@ static void thermal_zone_device_check(struct work_struct *work)
#define to_thermal_zone(_dev) \
container_of(_dev, struct thermal_zone_device, device)
+#define to_thermal_sensor(_dev) \
+ container_of(_dev, struct thermal_sensor, device)
+
+static ssize_t
+sensor_name_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct thermal_sensor *ts = to_thermal_sensor(dev);
+
+ return sprintf(buf, "%s\n", ts->name);
+}
+
+static ssize_t
+sensor_temp_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int ret;
+ long val;
+ struct thermal_sensor *ts = to_thermal_sensor(dev);
+
+ ret = ts->ops->get_temp(ts, &val);
+
+ return ret ? ret : sprintf(buf, "%ld\n", val);
+}
+
+static ssize_t
+hyst_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int indx, ret;
+ long val;
+ struct thermal_sensor *ts = to_thermal_sensor(dev);
+
+ if (!sscanf(attr->attr.name, "threshold%d_hyst", &indx))
+ return -EINVAL;
+
+ ret = ts->ops->get_hyst(ts, indx, &val);
+
+ return ret ? ret : sprintf(buf, "%ld\n", val);
+}
+
+static ssize_t
+hyst_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int indx, ret;
+ long val;
+ struct thermal_sensor *ts = to_thermal_sensor(dev);
+
+ if (!ts->ops->set_hyst)
+ return -EPERM;
+
+ if (!sscanf(attr->attr.name, "threshold%d_hyst", &indx))
+ return -EINVAL;
+
+ if (kstrtol(buf, 10, &val))
+ return -EINVAL;
+
+ ret = ts->ops->set_hyst(ts, indx, val);
+
+ return ret ? ret : count;
+}
+
+static ssize_t
+threshold_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int indx, ret;
+ long val;
+ struct thermal_sensor *ts = to_thermal_sensor(dev);
+
+ if (!sscanf(attr->attr.name, "threshold%d", &indx))
+ return -EINVAL;
+
+ ret = ts->ops->get_threshold(ts, indx, &val);
+
+ return ret ? ret : sprintf(buf, "%ld\n", val);
+}
+
+static ssize_t
+threshold_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int indx, ret;
+ long val;
+ struct thermal_sensor *ts = to_thermal_sensor(dev);
+
+ if (!ts->ops->set_threshold)
+ return -EPERM;
+
+ if (!sscanf(attr->attr.name, "threshold%d", &indx))
+ return -EINVAL;
+
+ if (kstrtol(buf, 10, &val))
+ return -EINVAL;
+
+ ret = ts->ops->set_threshold(ts, indx, val);
+
+ return ret ? ret : count;
+}
+
static ssize_t
type_show(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -705,6 +805,10 @@ static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store);
static DEVICE_ATTR(policy, S_IRUGO | S_IWUSR, policy_show, policy_store);
+/* Thermal sensor attributes */
+static DEVICE_ATTR(sensor_name, 0444, sensor_name_show, NULL);
+static DEVICE_ATTR(temp_input, 0444, sensor_temp_show, NULL);
+
/* sys I/F for cooling device */
#define to_cooling_device(_dev) \
container_of(_dev, struct thermal_cooling_device, device)
@@ -1491,6 +1595,182 @@ static void remove_trip_attrs(struct thermal_zone_device *tz)
}
/**
+ * enable_sensor_thresholds - create sysfs nodes for thresholdX
+ * @ts: the thermal sensor
+ * @count: Number of thresholds supported by sensor hardware
+ *
+ * 'Thresholds' are temperatures programmed into the sensor hardware,
+ * on crossing which the sensor generates an interrupt. 'Trip points'
+ * are temperatures which the thermal manager/governor thinks, some
+ * action should be taken when the sensor reaches the value.
+ */
+static int enable_sensor_thresholds(struct thermal_sensor *ts, int count)
+{
+ int i;
+ int size = sizeof(struct thermal_attr) * count;
+
+ ts->thresh_attrs = kzalloc(size, GFP_KERNEL);
+ if (!ts->thresh_attrs)
+ return -ENOMEM;
+
+ if (ts->ops->get_hyst) {
+ ts->hyst_attrs = kzalloc(size, GFP_KERNEL);
+ if (!ts->hyst_attrs) {
+ kfree(ts->thresh_attrs);
+ return -ENOMEM;
+ }
+ }
+
+ ts->thresholds = count;
+
+ /* Create threshold attributes */
+ for (i = 0; i < count; i++) {
+ snprintf(ts->thresh_attrs[i].name, THERMAL_NAME_LENGTH,
+ "threshold%d", i);
+
+ sysfs_attr_init(&ts->thresh_attrs[i].attr.attr);
+ ts->thresh_attrs[i].attr.attr.name = ts->thresh_attrs[i].name;
+ ts->thresh_attrs[i].attr.attr.mode = S_IRUGO | S_IWUSR;
+ ts->thresh_attrs[i].attr.show = threshold_show;
+ ts->thresh_attrs[i].attr.store = threshold_store;
+
+ device_create_file(&ts->device, &ts->thresh_attrs[i].attr);
+
+ /* Create threshold_hyst attributes */
+ if (!ts->ops->get_hyst)
+ continue;
+
+ snprintf(ts->hyst_attrs[i].name, THERMAL_NAME_LENGTH,
+ "threshold%d_hyst", i);
+
+ sysfs_attr_init(&ts->hyst_attrs[i].attr.attr);
+ ts->hyst_attrs[i].attr.attr.name = ts->hyst_attrs[i].name;
+ ts->hyst_attrs[i].attr.attr.mode = S_IRUGO | S_IWUSR;
+ ts->hyst_attrs[i].attr.show = hyst_show;
+ ts->hyst_attrs[i].attr.store = hyst_store;
+
+ device_create_file(&ts->device, &ts->hyst_attrs[i].attr);
+ }
+ return 0;
+}
+
+/**
+ * thermal_sensor_register - register a new thermal sensor
+ * @name: name of the thermal sensor
+ * @count: Number of thresholds supported by hardware
+ * @ops: standard thermal sensor callbacks
+ * @devdata: private device data
+ */
+struct thermal_sensor *thermal_sensor_register(const char *name, int count,
+ struct thermal_sensor_ops *ops, void *devdata)
+{
+ struct thermal_sensor *ts;
+ int ret;
+
+ if (!name || (name && strlen(name) >= THERMAL_NAME_LENGTH))
+ return ERR_PTR(-EINVAL);
+
+ if (!ops || !ops->get_temp)
+ return ERR_PTR(-EINVAL);
+
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ return ERR_PTR(-ENOMEM);
+
+ idr_init(&ts->idr);
+ ret = get_idr(&thermal_sensor_idr, &thermal_idr_lock, &ts->id);
+ if (ret)
+ goto exit_free;
+
+ strcpy(ts->name, name);
+ ts->ops = ops;
+ ts->devdata = devdata;
+ ts->device.class = &thermal_class;
+
+ dev_set_name(&ts->device, "sensor%d", ts->id);
+ ret = device_register(&ts->device);
+ if (ret)
+ goto exit_idr;
+
+ ret = device_create_file(&ts->device, &dev_attr_sensor_name);
+ if (ret)
+ goto exit_unregister;
+
+ ret = device_create_file(&ts->device, &dev_attr_temp_input);
+ if (ret)
+ goto exit_name;
+
+ if (count > 0 && ts->ops->get_threshold) {
+ ret = enable_sensor_thresholds(ts, count);
+ if (ret)
+ goto exit_temp;
+ }
+
+ /* Add this sensor to the global list of sensors */
+ mutex_lock(&sensor_list_lock);
+ list_add_tail(&ts->node, &thermal_sensor_list);
+ mutex_unlock(&sensor_list_lock);
+
+ return ts;
+
+exit_temp:
+ device_remove_file(&ts->device, &dev_attr_temp_input);
+exit_name:
+ device_remove_file(&ts->device, &dev_attr_sensor_name);
+exit_unregister:
+ device_unregister(&ts->device);
+exit_idr:
+ release_idr(&thermal_sensor_idr, &thermal_idr_lock, ts->id);
+exit_free:
+ kfree(ts);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(thermal_sensor_register);
+
+void thermal_sensor_unregister(struct thermal_sensor *ts)
+{
+ int i;
+ struct thermal_sensor *pos, *next;
+ bool found = false;
+
+ if (!ts)
+ return;
+
+ mutex_lock(&sensor_list_lock);
+ list_for_each_entry_safe(pos, next, &thermal_sensor_list, node) {
+ if (pos == ts) {
+ list_del(&ts->node);
+ found = true;
+ break;
+ }
+ }
+ mutex_unlock(&sensor_list_lock);
+
+ if (!found)
+ return;
+
+ for (i = 0; i < ts->thresholds; i++) {
+ device_remove_file(&ts->device, &ts->thresh_attrs[i].attr);
+ if (ts->ops->get_hyst) {
+ device_remove_file(&ts->device,
+ &ts->hyst_attrs[i].attr);
+ }
+ }
+
+ device_remove_file(&ts->device, &dev_attr_sensor_name);
+ device_remove_file(&ts->device, &dev_attr_temp_input);
+
+ release_idr(&thermal_sensor_idr, &thermal_idr_lock, ts->id);
+ idr_destroy(&ts->idr);
+
+ device_unregister(&ts->device);
+
+ kfree(ts);
+ return;
+}
+EXPORT_SYMBOL(thermal_sensor_unregister);
+
+/**
* thermal_zone_device_register - register a new thermal zone device
* @type: the thermal zone device type
* @trips: the number of trip points the thermal zone support
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 807f214..a49cb38 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -49,6 +49,7 @@
/* Default Thermal Governor: Does Linear Throttling */
#define DEFAULT_THERMAL_GOVERNOR "step_wise"
+struct thermal_sensor;
struct thermal_zone_device;
struct thermal_cooling_device;
@@ -127,6 +128,15 @@ struct thermal_cooling_device_ops {
int (*set_cur_state) (struct thermal_cooling_device *, unsigned long);
};
+struct thermal_sensor_ops {
+ int (*get_temp) (struct thermal_sensor *, long *);
+ int (*get_trend) (struct thermal_sensor *, int, enum thermal_trend *);
+ int (*set_threshold) (struct thermal_sensor *, int, long);
+ int (*get_threshold) (struct thermal_sensor *, int, long *);
+ int (*set_hyst) (struct thermal_sensor *, int, long);
+ int (*get_hyst) (struct thermal_sensor *, int, long *);
+};
+
struct thermal_cooling_device {
int id;
char type[THERMAL_NAME_LENGTH];
@@ -144,6 +154,21 @@ struct thermal_attr {
char name[THERMAL_NAME_LENGTH];
};
+struct thermal_sensor {
+ char name[THERMAL_NAME_LENGTH];
+ int id;
+ int temp;
+ int prev_temp;
+ int thresholds;
+ void *devdata;
+ struct idr idr;
+ struct device device;
+ struct list_head node;
+ struct thermal_sensor_ops *ops;
+ struct thermal_attr *thresh_attrs;
+ struct thermal_attr *hyst_attrs;
+};
+
struct thermal_zone_device {
int id;
char type[THERMAL_NAME_LENGTH];
@@ -237,6 +262,10 @@ void notify_thermal_framework(struct thermal_zone_device *, int);
int thermal_register_governor(struct thermal_governor *);
void thermal_unregister_governor(struct thermal_governor *);
+struct thermal_sensor *thermal_sensor_register(const char *, int,
+ struct thermal_sensor_ops *, void *);
+void thermal_sensor_unregister(struct thermal_sensor *);
+
#ifdef CONFIG_NET
extern int thermal_generate_netlink_event(u32 orig, enum events event);
#else
--
1.7.9.5
^ permalink raw reply related
* [PATCH 2/2] mmc: sdhci: fix dummy regulator issue
From: Kevin Liu @ 2013-01-07 5:38 UTC (permalink / raw)
To: linux-pm, Mark Brown, linux-mmc, Chris Ball
Cc: Marek Szyprowski, Rainer Kaluscha, Rainer Kaluscha, Philip Rakity,
Zhangfei Gao, Haojian Zhuang, Chao Xie, Kevin Liu, Kevin Liu
In-Reply-To: <1357537131-8000-1-git-send-email-kliu5@marvell.com>
Should consider the case that dummy regulator is used for host->vmmc
and host->vqmmc and skip the regulator voltage check.
Signed-off-by: Kevin Liu <kliu5@marvell.com>
---
drivers/mmc/host/sdhci.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index c13415c..561d126 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2897,8 +2897,8 @@ int sdhci_add_host(struct sdhci_host *host)
}
} else {
regulator_enable(host->vqmmc);
- if (!regulator_is_supported_voltage(host->vqmmc, 1700000,
- 1950000))
+ if ((regulator_is_supported_voltage(host->vqmmc, 1700000,
+ 1950000) <= 0) && !regulator_is_dummy(host->vqmmc))
caps[1] &= ~(SDHCI_SUPPORT_SDR104 |
SDHCI_SUPPORT_SDR50 |
SDHCI_SUPPORT_DDR50);
@@ -2965,7 +2965,7 @@ int sdhci_add_host(struct sdhci_host *host)
}
#ifdef CONFIG_REGULATOR
- if (host->vmmc) {
+ if (host->vmmc && !regulator_is_dummy(host->vmmc)) {
ret = regulator_is_supported_voltage(host->vmmc, 2700000,
3600000);
if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_330)))
--
1.7.9.5
^ permalink raw reply related
* [PATCH 1/2] regulator: add regulator_is_dummy function
From: Kevin Liu @ 2013-01-07 5:38 UTC (permalink / raw)
To: linux-pm, Mark Brown, linux-mmc, Chris Ball
Cc: Marek Szyprowski, Rainer Kaluscha, Rainer Kaluscha, Philip Rakity,
Zhangfei Gao, Haojian Zhuang, Chao Xie, Kevin Liu, Kevin Liu
Introduce a regulator_is_dummy function to check whether the
regulator is dummy.
Signed-off-by: Kevin Liu <kliu5@marvell.com>
---
drivers/regulator/core.c | 17 +++++++++++++++++
include/linux/regulator/consumer.h | 6 ++++++
2 files changed, 23 insertions(+)
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index e872c8b..d2a8bdb 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -1826,6 +1826,23 @@ int regulator_disable_regmap(struct regulator_dev *rdev)
}
EXPORT_SYMBOL_GPL(regulator_disable_regmap);
+/**
+ * regulator_is_dummy - check if regulator is dummy
+ * @regulator: regulator source
+ *
+ * Returns positive if the regulator is dummy, zero if it's not.
+ */
+int regulator_is_dummy(struct regulator *regulator)
+{
+ struct regulator_dev *rdev = regulator->rdev;
+
+ if (!strcmp(rdev_get_name(rdev), "regulator-dummy"))
+ return 1;
+ else
+ return 0;
+}
+EXPORT_SYMBOL_GPL(regulator_is_dummy);
+
static int _regulator_is_enabled(struct regulator_dev *rdev)
{
/* A GPIO control always takes precedence */
diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h
index c43cd35..fe549d0 100644
--- a/include/linux/regulator/consumer.h
+++ b/include/linux/regulator/consumer.h
@@ -164,6 +164,7 @@ int regulator_count_voltages(struct regulator *regulator);
int regulator_list_voltage(struct regulator *regulator, unsigned selector);
int regulator_is_supported_voltage(struct regulator *regulator,
int min_uV, int max_uV);
+int regulator_is_dummy(struct regulator *regulator);
int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV);
int regulator_set_voltage_time(struct regulator *regulator,
int old_uV, int new_uV);
@@ -249,6 +250,11 @@ static inline int regulator_is_enabled(struct regulator *regulator)
return 1;
}
+static inline int regulator_is_dummy(struct regulator *regulator)
+{
+ return 0;
+}
+
static inline int regulator_bulk_get(struct device *dev,
int num_consumers,
struct regulator_bulk_data *consumers)
--
1.7.9.5
^ permalink raw reply related
* Re: [PATCH v11 9/9] libata: do not suspend port if normal ODD is attached
From: Aaron Lu @ 2013-01-07 1:09 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: Jeff Garzik, James Bottomley, Rafael J. Wysocki, Alan Stern,
Tejun Heo, Aaron Lu, Jeff Wu, linux-ide, linux-pm, linux-scsi,
linux-acpi
In-Reply-To: <50E98B7C.7030908@mvista.com>
Hi Sergei,
Thanks for reviewing, will update in next revision.
-Aaron
On 01/06/2013 10:34 PM, Sergei Shtylyov wrote:
> Hello.
>
> On 06-01-2013 6:48, Aaron Lu wrote:
>
>> For ODDs, the upper layer will poll for media change every a few
>> seconds, which will make it enter and leave suspend state very
>> oftern. And as each suspend will also cause a hard/soft reset,
>
> s/oftern/often/
>
>> the gain of runtime suspend is very little while the ODD may
>> mis-function after constantly being reset. So the idle callback
>> here will not proceed to suspend if a non-ZPODD capable ODD is
>> attached to the port.
>
>> Signed-off-by: Aaron Lu <aaron.lu@intel.com>
>> ---
>> drivers/ata/libata-core.c | 19 +++++++++++++++++++
>> 1 file changed, 19 insertions(+)
>
>> diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
>> index 65a362e..f664a90 100644
>> --- a/drivers/ata/libata-core.c
>> +++ b/drivers/ata/libata-core.c
>> @@ -5408,8 +5408,27 @@ static int ata_port_resume(struct device *dev)
>> return rc;
>> }
>>
>> +/*
>> + * For ODDs, the upper layer will poll for media change every a few seconds,
>> + * which will make it enter and leave suspend state every a few seconds. And
>
> Article is not needed before "few". (Same comment to the changelog.)
>
>> + * as each suspend will cause a hard/soft reset, the gain of runtime suspend
>> + * is very little and the ODD may mis-function after constantly being reset.
>
> s/mis-function/malfunction/ perhaps? (Same in the changelog.)
>
> MBR, Sergei
>
^ permalink raw reply
* [PATCH 1/2] thermal: sysfs: Add a new sysfs node emul_temp
From: Amit Daniel Kachhap @ 2013-01-07 0:08 UTC (permalink / raw)
To: linux-pm, Zhang Rui; +Cc: jonghwa3.lee, linux-samsung-soc, linux-kernel
This patch adds support to set the emulated temperature method in
thermal zone (sensor). After setting this feature thermal zone must
report this temperature and not the actual temperature. The actual
implementation of this emulated temperature is based on sensor
capability or platform specific. This is useful in debugging different
temperature threshold and its associated cooling action. Writing 0 on
this node should disable emulation.
Signed-off-by: Amit Daniel Kachhap <amit.daniel@samsung.com>
---
Documentation/thermal/sysfs-api.txt | 14 ++++++++++++++
drivers/thermal/thermal_sys.c | 26 ++++++++++++++++++++++++++
include/linux/thermal.h | 1 +
3 files changed, 41 insertions(+), 0 deletions(-)
diff --git a/Documentation/thermal/sysfs-api.txt b/Documentation/thermal/sysfs-api.txt
index 88c0233..e8f2ee4 100644
--- a/Documentation/thermal/sysfs-api.txt
+++ b/Documentation/thermal/sysfs-api.txt
@@ -55,6 +55,8 @@ temperature) and throttle appropriate devices.
.get_trip_type: get the type of certain trip point.
.get_trip_temp: get the temperature above which the certain trip point
will be fired.
+ .set_emul_temp: set the emulation temperature which helps in debugging
+ different threshold temperature points.
1.1.2 void thermal_zone_device_unregister(struct thermal_zone_device *tz)
@@ -153,6 +155,7 @@ Thermal zone device sys I/F, created once it's registered:
|---trip_point_[0-*]_temp: Trip point temperature
|---trip_point_[0-*]_type: Trip point type
|---trip_point_[0-*]_hyst: Hysteresis value for this trip point
+ |---emul_temp: Emulated temperature set node
Thermal cooling device sys I/F, created once it's registered:
/sys/class/thermal/cooling_device[0-*]:
@@ -252,6 +255,17 @@ passive
Valid values: 0 (disabled) or greater than 1000
RW, Optional
+emul_temp
+ Interface to set the emulated temperature method in thermal zone
+ (sensor). After setting this feature thermal zone must report
+ this temperature and not the actual temperature. The actual
+ implementation of this emulated temperature is platform specific.
+ This is useful in debugging different temperature threshold and its
+ associated cooling action. Writing 0 on this node should disable
+ emulation.
+ Unit: millidegree Celsius
+ WO, Optional
+
*****************************
* Cooling device attributes *
*****************************
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index 8c8ce80..ecdfc7d 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -700,11 +700,31 @@ policy_show(struct device *dev, struct device_attribute *devattr, char *buf)
return sprintf(buf, "%s\n", tz->governor->name);
}
+static ssize_t
+emul_temp_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct thermal_zone_device *tz = to_thermal_zone(dev);
+ int ret;
+ unsigned long temperature;
+
+ if (!tz->ops->set_emul_temp)
+ return -EPERM;
+
+ if (kstrtoul(buf, 10, &temperature))
+ return -EINVAL;
+
+ ret = tz->ops->set_emul_temp(tz, temperature);
+
+ return ret ? ret : count;
+}
+
static DEVICE_ATTR(type, 0444, type_show, NULL);
static DEVICE_ATTR(temp, 0444, temp_show, NULL);
static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store);
static DEVICE_ATTR(policy, S_IRUGO | S_IWUSR, policy_show, policy_store);
+static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store);
/* sys I/F for cooling device */
#define to_cooling_device(_dev) \
@@ -1592,6 +1612,12 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
goto unregister;
}
+ if (ops->set_emul_temp) {
+ result = device_create_file(&tz->device, &dev_attr_emul_temp);
+ if (result)
+ goto unregister;
+ }
+
/* Create policy attribute */
result = device_create_file(&tz->device, &dev_attr_policy);
if (result)
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 883bcda..fbb87d4 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -123,6 +123,7 @@ struct thermal_zone_device_ops {
int (*set_trip_hyst) (struct thermal_zone_device *, int,
unsigned long);
int (*get_crit_temp) (struct thermal_zone_device *, unsigned long *);
+ int (*set_emul_temp) (struct thermal_zone_device *, unsigned long);
int (*get_trend) (struct thermal_zone_device *, int,
enum thermal_trend *);
int (*notify) (struct thermal_zone_device *, int,
--
1.7.5.4
^ permalink raw reply related
* [PATCH 2/2] thermal: exynos: Use the framework for temperature emulation support
From: Amit Daniel Kachhap @ 2013-01-07 0:08 UTC (permalink / raw)
To: linux-pm, Zhang Rui; +Cc: jonghwa3.lee, linux-samsung-soc, linux-kernel
In-Reply-To: <1357517296-31402-1-git-send-email-amit.daniel@samsung.com>
This removes the driver specific sysfs support of the temperature
emulation and uses the newly added core thermal framework for thermal
emulation. A platform specific handler is added to support this.
Signed-off-by: Amit Daniel Kachhap <amit.daniel@samsung.com>
---
Documentation/thermal/exynos_thermal_emulation | 6 +-
drivers/thermal/exynos_thermal.c | 154 ++++++++++--------------
2 files changed, 64 insertions(+), 96 deletions(-)
diff --git a/Documentation/thermal/exynos_thermal_emulation b/Documentation/thermal/exynos_thermal_emulation
index b73bbfb..570a52d 100644
--- a/Documentation/thermal/exynos_thermal_emulation
+++ b/Documentation/thermal/exynos_thermal_emulation
@@ -14,10 +14,10 @@ manually with software code and TMU will read current temperature from user valu
sensor's value.
Enabling CONFIG_EXYNOS_THERMAL_EMUL option will make this support in available.
-When it's enabled, sysfs node will be created under
-/sys/bus/platform/devices/'exynos device name'/ with name of 'emulation'.
+When it's enabled, sysfs node will be created as
+/sys/devices/virtual/thermal/thermal_zone'zone id'/emul_temp.
-The sysfs node, 'emulation', will contain value 0 for the initial state. When you input any
+The sysfs node, 'emul_node', will contain value 0 for the initial state. When you input any
temperature you want to update to sysfs node, it automatically enable emulation mode and
current temperature will be changed into it.
(Exynos also supports user changable delay time which would be used to delay of
diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c
index 68c9280..a657903 100644
--- a/drivers/thermal/exynos_thermal.c
+++ b/drivers/thermal/exynos_thermal.c
@@ -145,6 +145,7 @@ struct thermal_cooling_conf {
struct thermal_sensor_conf {
char name[SENSOR_NAME_LEN];
int (*read_temperature)(void *data);
+ int (*write_emul_temp)(void *data, unsigned long temp);
struct thermal_trip_point_conf trip_data;
struct thermal_cooling_conf cooling_data;
void *private_data;
@@ -369,6 +370,23 @@ static int exynos_get_temp(struct thermal_zone_device *thermal,
return 0;
}
+/* Get temperature callback functions for thermal zone */
+static int exynos_set_emul_temp(struct thermal_zone_device *thermal,
+ unsigned long temp)
+{
+ void *data;
+ int ret = -EINVAL;
+
+ if (!th_zone->sensor_conf) {
+ pr_info("Temperature sensor not initialised\n");
+ return -EINVAL;
+ }
+ data = th_zone->sensor_conf->private_data;
+ if (th_zone->sensor_conf->write_emul_temp)
+ ret = th_zone->sensor_conf->write_emul_temp(data, temp);
+ return ret;
+}
+
/* Get the temperature trend */
static int exynos_get_trend(struct thermal_zone_device *thermal,
int trip, enum thermal_trend *trend)
@@ -392,6 +410,7 @@ static struct thermal_zone_device_ops const exynos_dev_ops = {
.bind = exynos_bind,
.unbind = exynos_unbind,
.get_temp = exynos_get_temp,
+ .set_emul_temp = exynos_set_emul_temp,
.get_trend = exynos_get_trend,
.get_mode = exynos_get_mode,
.set_mode = exynos_set_mode,
@@ -714,6 +733,44 @@ static int exynos_tmu_read(struct exynos_tmu_data *data)
return temp;
}
+#ifdef CONFIG_EXYNOS_THERMAL_EMUL
+static int exynos_tmu_set_emulation(struct exynos_tmu_data *data,
+ unsigned long temp)
+{
+ unsigned int reg;
+ int ret = -EINVAL;
+
+ if (data->soc == SOC_ARCH_EXYNOS4210)
+ goto out;
+
+ if (temp && temp < MCELSIUS)
+ goto out;
+
+ mutex_lock(&data->lock);
+ clk_enable(data->clk);
+
+ reg = readl(data->base + EXYNOS_EMUL_CON);
+
+ if (temp) {
+ temp /= MCELSIUS;
+
+ reg = (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT) |
+ (temp_to_code(data, temp)
+ << EXYNOS_EMUL_DATA_SHIFT) | EXYNOS_EMUL_ENABLE;
+ } else {
+ reg &= ~EXYNOS_EMUL_ENABLE;
+ }
+
+ writel(reg, data->base + EXYNOS_EMUL_CON);
+
+ clk_disable(data->clk);
+ mutex_unlock(&data->lock);
+ return 0;
+out:
+ return ret;
+}
+#endif/*CONFIG_EXYNOS_THERMAL_EMUL*/
+
static void exynos_tmu_work(struct work_struct *work)
{
struct exynos_tmu_data *data = container_of(work,
@@ -853,93 +910,6 @@ static inline struct exynos_tmu_platform_data *exynos_get_driver_data(
platform_get_device_id(pdev)->driver_data;
}
-#ifdef CONFIG_EXYNOS_THERMAL_EMUL
-static ssize_t exynos_tmu_emulation_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct platform_device *pdev = container_of(dev,
- struct platform_device, dev);
- struct exynos_tmu_data *data = platform_get_drvdata(pdev);
- unsigned int reg;
- u8 temp_code;
- int temp = 0;
-
- if (data->soc == SOC_ARCH_EXYNOS4210)
- goto out;
-
- mutex_lock(&data->lock);
- clk_enable(data->clk);
- reg = readl(data->base + EXYNOS_EMUL_CON);
- clk_disable(data->clk);
- mutex_unlock(&data->lock);
-
- if (reg & EXYNOS_EMUL_ENABLE) {
- reg >>= EXYNOS_EMUL_DATA_SHIFT;
- temp_code = reg & EXYNOS_EMUL_DATA_MASK;
- temp = code_to_temp(data, temp_code);
- }
-out:
- return sprintf(buf, "%d\n", temp * MCELSIUS);
-}
-
-static ssize_t exynos_tmu_emulation_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct platform_device *pdev = container_of(dev,
- struct platform_device, dev);
- struct exynos_tmu_data *data = platform_get_drvdata(pdev);
- unsigned int reg;
- int temp;
-
- if (data->soc == SOC_ARCH_EXYNOS4210)
- goto out;
-
- if (!sscanf(buf, "%d\n", &temp) || temp < 0)
- return -EINVAL;
-
- mutex_lock(&data->lock);
- clk_enable(data->clk);
-
- reg = readl(data->base + EXYNOS_EMUL_CON);
-
- if (temp) {
- /* Both CELSIUS and MCELSIUS type are available for input */
- if (temp > MCELSIUS)
- temp /= MCELSIUS;
-
- reg = (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT) |
- (temp_to_code(data, (temp / MCELSIUS))
- << EXYNOS_EMUL_DATA_SHIFT) | EXYNOS_EMUL_ENABLE;
- } else {
- reg &= ~EXYNOS_EMUL_ENABLE;
- }
-
- writel(reg, data->base + EXYNOS_EMUL_CON);
-
- clk_disable(data->clk);
- mutex_unlock(&data->lock);
-
-out:
- return count;
-}
-
-static DEVICE_ATTR(emulation, 0644, exynos_tmu_emulation_show,
- exynos_tmu_emulation_store);
-static int create_emulation_sysfs(struct device *dev)
-{
- return device_create_file(dev, &dev_attr_emulation);
-}
-static void remove_emulation_sysfs(struct device *dev)
-{
- device_remove_file(dev, &dev_attr_emulation);
-}
-#else
-static inline int create_emulation_sysfs(struct device *dev) { return 0; }
-static inline void remove_emulation_sysfs(struct device *dev) {}
-#endif
-
static int __devinit exynos_tmu_probe(struct platform_device *pdev)
{
struct exynos_tmu_data *data;
@@ -1016,6 +986,10 @@ static int __devinit exynos_tmu_probe(struct platform_device *pdev)
/* Register the sensor with thermal management interface */
(&exynos_sensor_conf)->private_data = data;
+#ifdef CONFIG_EXYNOS_THERMAL_EMUL
+ (&exynos_sensor_conf)->write_emul_temp =
+ (int (*)(void *, unsigned long))exynos_tmu_set_emulation;
+#endif
exynos_sensor_conf.trip_data.trip_count = pdata->trigger_level0_en +
pdata->trigger_level1_en + pdata->trigger_level2_en +
pdata->trigger_level3_en;
@@ -1041,10 +1015,6 @@ static int __devinit exynos_tmu_probe(struct platform_device *pdev)
goto err_clk;
}
- ret = create_emulation_sysfs(&pdev->dev);
- if (ret)
- dev_err(&pdev->dev, "Failed to create emulation mode sysfs node\n");
-
return 0;
err_clk:
platform_set_drvdata(pdev, NULL);
@@ -1056,8 +1026,6 @@ static int __devexit exynos_tmu_remove(struct platform_device *pdev)
{
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
- remove_emulation_sysfs(&pdev->dev);
-
exynos_tmu_control(pdev, false);
exynos_unregister_thermal();
--
1.7.5.4
^ permalink raw reply related
* Re: [PATCH 2/5 RESEND] thermal: exynos: Miscellaneous fixes to support falling threshold interrupt
From: Joe Perches @ 2013-01-06 23:55 UTC (permalink / raw)
To: Amit Daniel Kachhap
Cc: linux-pm, linux-pm, Zhang Rui, linux-samsung-soc, linux-kernel
In-Reply-To: <1357516248-31350-1-git-send-email-amit.daniel@samsung.com>
On Sun, 2013-01-06 at 15:50 -0800, Amit Daniel Kachhap wrote:
> Below fixes are done to support falling threshold interrupt,
> * Falling interrupt status macro corrected according to exynos5 data sheet.
> * The get trend function modified to calculate trip temperature correctly.
> * The clearing of interrupt status in the isr is now done after handling
> the event that caused the interrupt.
[]
> diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c
[]
> @@ -373,12 +373,19 @@ static int exynos_get_temp(struct thermal_zone_device *thermal,
> static int exynos_get_trend(struct thermal_zone_device *thermal,
> int trip, enum thermal_trend *trend)
> {
> - if (thermal->temperature >= trip)
> + int ret = 0;
Unnecessary initialization
> + unsigned long trip_temp;
> +
> + ret = exynos_get_trip_temp(thermal, trip, &trip_temp);
> + if (ret < 0)
> + return ret;
> +
> + if (thermal->temperature >= trip_temp)
> *trend = THERMAL_TREND_RAISING;
> else
> *trend = THERMAL_TREND_DROPPING;
THERMAL_TREND_STABLE ?
>
> - return 0;
> + return ret;
return 0 is clearer.
^ permalink raw reply
* [PATCH 2/5 RESEND] thermal: exynos: Miscellaneous fixes to support falling threshold interrupt
From: Amit Daniel Kachhap @ 2013-01-06 23:50 UTC (permalink / raw)
To: linux-pm, linux-pm, Zhang Rui; +Cc: linux-samsung-soc, linux-kernel
In-Reply-To: <1353937640-9939-3-git-send-email-amit.kachhap@linaro.org>
Below fixes are done to support falling threshold interrupt,
* Falling interrupt status macro corrected according to exynos5 data sheet.
* The get trend function modified to calculate trip temperature correctly.
* The clearing of interrupt status in the isr is now done after handling
the event that caused the interrupt.
Signed-off-by: Amit Daniel Kachhap <amit.daniel@samsung.com>
---
drivers/thermal/exynos_thermal.c | 19 ++++++++++++-------
1 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c
index dcd13f7..0d17d41 100644
--- a/drivers/thermal/exynos_thermal.c
+++ b/drivers/thermal/exynos_thermal.c
@@ -82,7 +82,7 @@
#define EXYNOS_TRIMINFO_RELOAD 0x1
#define EXYNOS_TMU_CLEAR_RISE_INT 0x111
-#define EXYNOS_TMU_CLEAR_FALL_INT (0x111 << 16)
+#define EXYNOS_TMU_CLEAR_FALL_INT (0x111 << 12)
#define EXYNOS_MUX_ADDR_VALUE 6
#define EXYNOS_MUX_ADDR_SHIFT 20
#define EXYNOS_TMU_TRIP_MODE_SHIFT 13
@@ -373,12 +373,19 @@ static int exynos_get_temp(struct thermal_zone_device *thermal,
static int exynos_get_trend(struct thermal_zone_device *thermal,
int trip, enum thermal_trend *trend)
{
- if (thermal->temperature >= trip)
+ int ret = 0;
+ unsigned long trip_temp;
+
+ ret = exynos_get_trip_temp(thermal, trip, &trip_temp);
+ if (ret < 0)
+ return ret;
+
+ if (thermal->temperature >= trip_temp)
*trend = THERMAL_TREND_RAISING;
else
*trend = THERMAL_TREND_DROPPING;
- return 0;
+ return ret;
}
/* Operation callback functions for thermal zone */
static struct thermal_zone_device_ops const exynos_dev_ops = {
@@ -712,10 +719,9 @@ static void exynos_tmu_work(struct work_struct *work)
struct exynos_tmu_data *data = container_of(work,
struct exynos_tmu_data, irq_work);
+ exynos_report_trigger();
mutex_lock(&data->lock);
clk_enable(data->clk);
-
-
if (data->soc == SOC_ARCH_EXYNOS)
writel(EXYNOS_TMU_CLEAR_RISE_INT |
EXYNOS_TMU_CLEAR_FALL_INT,
@@ -723,10 +729,9 @@ static void exynos_tmu_work(struct work_struct *work)
else
writel(EXYNOS4210_TMU_INTCLEAR_VAL,
data->base + EXYNOS_TMU_REG_INTCLEAR);
-
clk_disable(data->clk);
mutex_unlock(&data->lock);
- exynos_report_trigger();
+
enable_irq(data->irq);
}
--
1.7.5.4
^ permalink raw reply related
* Re: [PATCH] cpuidle - fix lock contention in the idle path
From: Daniel Lezcano @ 2013-01-06 23:07 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: Russ Anderson, linux-pm, pdeschrijver, akpm, linux-kernel, rja
In-Reply-To: <3077900.eue4T2ZoF8@vostro.rjw.lan>
[ ... ]
>>>> The following patch is a hot fix by returning to the initial behavior
>>>> by removing the lock when getting the driver.
>>>
>>> The patch fixes the problem. Verified on a system with 1024 cpus.
>>> Thanks.
>>>
>>>> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
>>>
>>> Reported-by: Russ Anderson <rja@sgi.com>
>>> Acked-by: Russ Anderson <rja@sgi.com>
>>
>> Hi Rafael,
>>
>> could you consider this patch for merging ?
>
> Yes, I've taken it already.
>
> I'll include it into the next pull request.
Ok, thanks.
I noticed you rephrased the changelog. Thanks for that also.
-- Daniel
--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs
Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog
* English - detected
* English
* French
* English
* French
<javascript:void(0);> <#>
^ permalink raw reply
* Re: [PATCH] power: max17040: use devm_kzalloc
From: Anton Vorontsov @ 2013-01-06 22:21 UTC (permalink / raw)
To: Devendra Naga; +Cc: David Woodhouse, linux-pm
In-Reply-To: <1357448005-20896-1-git-send-email-devendra.aaru@gmail.com>
On Sat, Jan 05, 2013 at 11:53:25PM -0500, Devendra Naga wrote:
> use devm_kzalloc and no need of error path and unload frees
>
> Signed-off-by: Devendra Naga <devendra.aaru@gmail.com>
> ---
Applied, thanks.
^ permalink raw reply
* Re: [PATCH/RFC 0/1] Delete legacy power trace API
From: Rafael J. Wysocki @ 2013-01-06 22:28 UTC (permalink / raw)
To: Steven Rostedt
Cc: Paul Gortmaker, linux-pm, Arjan van de Ven, Frederic Weisbecker,
linux-kernel
In-Reply-To: <1357510436.10284.69.camel@gandalf.local.home>
On Sunday, January 06, 2013 05:13:56 PM Steven Rostedt wrote:
> On Sat, 2013-01-05 at 23:10 +0100, Rafael J. Wysocki wrote:
> > On Friday, January 04, 2013 08:49:03 PM Paul Gortmaker wrote:
> > > The actual deletion is mind-numbingly simple; and if you go by the
> > > comments in the code, it is well overdue. However, in discussions
> > > with Frederic, he suggested to me that those comments might have
> > > been overly optimistic, and that there may still be people out
> > > there who are still unknowingly using this dead API.
> > >
> > > So, that is the crux of the RFC component -- to check whether the
> > > comments saying "delete by v3.1" can be taken at face value, or
> > > whether they were overly optimistic, and hence this stuff is still
> > > actively used even though it is overdue for deletion.
> >
> > Do you want me or the tracing maintainers to handle this?
> >
>
> You can take it. If you want you can add:
>
> Acked-by: Steven Rostedt <rostedt@goodmis.org>
Cool, that helps. :-)
I'll take it.
Thanks,
Rafael
--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
^ permalink raw reply
* Re: [PATCH/RFC 0/1] Delete legacy power trace API
From: Steven Rostedt @ 2013-01-06 22:13 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: Paul Gortmaker, linux-pm, Arjan van de Ven, Frederic Weisbecker,
linux-kernel
In-Reply-To: <5190666.Mu6UtpjCCL@vostro.rjw.lan>
On Sat, 2013-01-05 at 23:10 +0100, Rafael J. Wysocki wrote:
> On Friday, January 04, 2013 08:49:03 PM Paul Gortmaker wrote:
> > The actual deletion is mind-numbingly simple; and if you go by the
> > comments in the code, it is well overdue. However, in discussions
> > with Frederic, he suggested to me that those comments might have
> > been overly optimistic, and that there may still be people out
> > there who are still unknowingly using this dead API.
> >
> > So, that is the crux of the RFC component -- to check whether the
> > comments saying "delete by v3.1" can be taken at face value, or
> > whether they were overly optimistic, and hence this stuff is still
> > actively used even though it is overdue for deletion.
>
> Do you want me or the tracing maintainers to handle this?
>
You can take it. If you want you can add:
Acked-by: Steven Rostedt <rostedt@goodmis.org>
-- Steve
^ permalink raw reply
* Re: [PATCH v11 9/9] libata: do not suspend port if normal ODD is attached
From: Sergei Shtylyov @ 2013-01-06 14:34 UTC (permalink / raw)
To: Aaron Lu
Cc: Jeff Garzik, James Bottomley, Rafael J. Wysocki, Alan Stern,
Tejun Heo, Aaron Lu, Jeff Wu, linux-ide, linux-pm, linux-scsi,
linux-acpi
In-Reply-To: <1357440509-28108-10-git-send-email-aaron.lu@intel.com>
Hello.
On 06-01-2013 6:48, Aaron Lu wrote:
> For ODDs, the upper layer will poll for media change every a few
> seconds, which will make it enter and leave suspend state very
> oftern. And as each suspend will also cause a hard/soft reset,
s/oftern/often/
> the gain of runtime suspend is very little while the ODD may
> mis-function after constantly being reset. So the idle callback
> here will not proceed to suspend if a non-ZPODD capable ODD is
> attached to the port.
> Signed-off-by: Aaron Lu <aaron.lu@intel.com>
> ---
> drivers/ata/libata-core.c | 19 +++++++++++++++++++
> 1 file changed, 19 insertions(+)
> diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
> index 65a362e..f664a90 100644
> --- a/drivers/ata/libata-core.c
> +++ b/drivers/ata/libata-core.c
> @@ -5408,8 +5408,27 @@ static int ata_port_resume(struct device *dev)
> return rc;
> }
>
> +/*
> + * For ODDs, the upper layer will poll for media change every a few seconds,
> + * which will make it enter and leave suspend state every a few seconds. And
Article is not needed before "few". (Same comment to the changelog.)
> + * as each suspend will cause a hard/soft reset, the gain of runtime suspend
> + * is very little and the ODD may mis-function after constantly being reset.
s/mis-function/malfunction/ perhaps? (Same in the changelog.)
MBR, Sergei
^ permalink raw reply
* [PATCH v6 4/4] sd: change to auto suspend mode
From: Aaron Lu @ 2013-01-06 8:41 UTC (permalink / raw)
To: Alan Stern, Jens Axboe, Rafael J. Wysocki, James Bottomley
Cc: linux-pm, linux-scsi, linux-kernel, Aaron Lu, Aaron Lu,
Shane Huang
In-Reply-To: <1357461697-4219-1-git-send-email-aaron.lu@intel.com>
From: Lin Ming <ming.m.lin@intel.com>
Uses block layer runtime pm helper functions in
scsi_runtime_suspend/resume.
Remove scsi_autopm_* from sd open/release path and check_events path.
And remove the quiesce call in runtime suspend path, as we know there is
no request to quiesce for the device.
[aaron.lu@intel.com: Do not quiesce the device in runtime suspend]
[aaron.lu@intel.com: Remove scsi_autopm_* from sd_check_events]
[aaron.lu@intel.com: Move blk_pm_runtime_init to sdev init time]
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Aaron Lu <aaron.lu@intel.com>
---
drivers/scsi/scsi_pm.c | 35 +++++++++++++++++++++++------------
drivers/scsi/scsi_sysfs.c | 1 +
drivers/scsi/sd.c | 12 ------------
3 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/drivers/scsi/scsi_pm.c b/drivers/scsi/scsi_pm.c
index 8f6b12c..d5ddc2c 100644
--- a/drivers/scsi/scsi_pm.c
+++ b/drivers/scsi/scsi_pm.c
@@ -147,15 +147,19 @@ static int scsi_bus_restore(struct device *dev)
static int scsi_runtime_suspend(struct device *dev)
{
int err = 0;
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
dev_dbg(dev, "scsi_runtime_suspend\n");
if (scsi_is_sdev_device(dev)) {
- err = scsi_dev_type_suspend(dev,
- pm ? pm->runtime_suspend : NULL);
- if (err == -EAGAIN)
- pm_schedule_suspend(dev, jiffies_to_msecs(
- round_jiffies_up_relative(HZ/10)));
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct request_queue *q = sdev->request_queue;
+
+ err = blk_pre_runtime_suspend(q);
+ if (err)
+ return err;
+
+ err = pm_generic_runtime_suspend(dev);
+
+ blk_post_runtime_suspend(q, err);
}
/* Insert hooks here for targets, hosts, and transport classes */
@@ -166,11 +170,16 @@ static int scsi_runtime_suspend(struct device *dev)
static int scsi_runtime_resume(struct device *dev)
{
int err = 0;
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
dev_dbg(dev, "scsi_runtime_resume\n");
- if (scsi_is_sdev_device(dev))
- err = scsi_dev_type_resume(dev, pm ? pm->runtime_resume : NULL);
+ if (scsi_is_sdev_device(dev)) {
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct request_queue *q = sdev->request_queue;
+
+ blk_pre_runtime_resume(q);
+ err = pm_generic_runtime_resume(dev);
+ blk_post_runtime_resume(q, err);
+ }
/* Insert hooks here for targets, hosts, and transport classes */
@@ -185,10 +194,12 @@ static int scsi_runtime_idle(struct device *dev)
/* Insert hooks here for targets, hosts, and transport classes */
- if (scsi_is_sdev_device(dev))
- err = pm_schedule_suspend(dev, 100);
- else
+ if (scsi_is_sdev_device(dev)) {
+ pm_runtime_mark_last_busy(dev);
+ err = pm_runtime_autosuspend(dev);
+ } else {
err = pm_runtime_suspend(dev);
+ }
return err;
}
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 931a7d9..63384cf 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -1112,6 +1112,7 @@ void scsi_sysfs_device_initialize(struct scsi_device *sdev)
sdev->sdev_gendev.type = &scsi_dev_type;
dev_set_name(&sdev->sdev_gendev, "%d:%d:%d:%d",
sdev->host->host_no, sdev->channel, sdev->id, sdev->lun);
+ blk_pm_runtime_init(sdev->request_queue, &sdev->sdev_gendev);
device_initialize(&sdev->sdev_dev);
sdev->sdev_dev.parent = get_device(&sdev->sdev_gendev);
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 8ca160e..70c202c 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -1121,10 +1121,6 @@ static int sd_open(struct block_device *bdev, fmode_t mode)
sdev = sdkp->device;
- retval = scsi_autopm_get_device(sdev);
- if (retval)
- goto error_autopm;
-
/*
* If the device is in error recovery, wait until it is done.
* If the device is offline, then disallow any access to it.
@@ -1169,8 +1165,6 @@ static int sd_open(struct block_device *bdev, fmode_t mode)
return 0;
error_out:
- scsi_autopm_put_device(sdev);
-error_autopm:
scsi_disk_put(sdkp);
return retval;
}
@@ -1205,7 +1199,6 @@ static int sd_release(struct gendisk *disk, fmode_t mode)
* XXX is followed by a "rmmod sd_mod"?
*/
- scsi_autopm_put_device(sdev);
scsi_disk_put(sdkp);
return 0;
}
@@ -1367,14 +1360,9 @@ static unsigned int sd_check_events(struct gendisk *disk, unsigned int clearing)
retval = -ENODEV;
if (scsi_block_when_processing_errors(sdp)) {
- retval = scsi_autopm_get_device(sdp);
- if (retval)
- goto out;
-
sshdr = kzalloc(sizeof(*sshdr), GFP_KERNEL);
retval = scsi_test_unit_ready(sdp, SD_TIMEOUT, SD_MAX_RETRIES,
sshdr);
- scsi_autopm_put_device(sdp);
}
/* failed to execute TUR, assume media not present */
--
1.7.11.7
^ permalink raw reply related
* [PATCH v6 3/4] block: implement runtime pm strategy
From: Aaron Lu @ 2013-01-06 8:41 UTC (permalink / raw)
To: Alan Stern, Jens Axboe, Rafael J. Wysocki, James Bottomley
Cc: linux-pm, linux-scsi, linux-kernel, Aaron Lu, Aaron Lu,
Shane Huang
In-Reply-To: <1357461697-4219-1-git-send-email-aaron.lu@intel.com>
From: Lin Ming <ming.m.lin@intel.com>
When a request is added:
If device is suspended or is suspending and the request is not a
PM request, resume the device.
When the last request finishes:
Call pm_runtime_mark_last_busy() and pm_runtime_autosuspend().
When pick a request:
If device is resuming/suspending, then only PM request is allowed to go.
Return NULL for other cases.
[aaron.lu@intel.com: PM request does not involve nr_pending counting]
[aaron.lu@intel.com: No need to check q->dev]
[aaron.lu@intel.com: Autosuspend when the last request finished]
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Aaron Lu <aaron.lu@intel.com>
---
block/blk-core.c | 7 +++++++
block/elevator.c | 4 ++++
include/linux/blkdev.h | 41 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 52 insertions(+)
diff --git a/block/blk-core.c b/block/blk-core.c
index 6fc24bb..93c1461 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -1274,6 +1274,8 @@ void __blk_put_request(struct request_queue *q, struct request *req)
if (unlikely(--req->ref_count))
return;
+ blk_pm_put_request(req);
+
elv_completed_request(q, req);
/* this is a bio leak */
@@ -2080,6 +2082,11 @@ struct request *blk_peek_request(struct request_queue *q)
int ret;
while ((rq = __elv_next_request(q)) != NULL) {
+
+ rq = blk_pm_peek_request(q, rq);
+ if (!rq)
+ break;
+
if (!(rq->cmd_flags & REQ_STARTED)) {
/*
* This is the first time the device driver
diff --git a/block/elevator.c b/block/elevator.c
index 9edba1b..61e9b49 100644
--- a/block/elevator.c
+++ b/block/elevator.c
@@ -544,6 +544,8 @@ void elv_requeue_request(struct request_queue *q, struct request *rq)
rq->cmd_flags &= ~REQ_STARTED;
+ blk_pm_requeue_request(rq);
+
__elv_add_request(q, rq, ELEVATOR_INSERT_REQUEUE);
}
@@ -566,6 +568,8 @@ void __elv_add_request(struct request_queue *q, struct request *rq, int where)
{
trace_block_rq_insert(q, rq);
+ blk_pm_add_request(q, rq);
+
rq->q = q;
if (rq->cmd_flags & REQ_SOFTBARRIER) {
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index a96e144..884c405 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -974,6 +974,40 @@ extern int blk_pre_runtime_suspend(struct request_queue *q);
extern void blk_post_runtime_suspend(struct request_queue *q, int err);
extern void blk_pre_runtime_resume(struct request_queue *q);
extern void blk_post_runtime_resume(struct request_queue *q, int err);
+
+static inline void blk_pm_put_request(struct request *rq)
+{
+ if (!(rq->cmd_flags & REQ_PM) && !--rq->q->nr_pending) {
+ pm_runtime_mark_last_busy(rq->q->dev);
+ pm_runtime_autosuspend(rq->q->dev);
+ }
+}
+
+static inline struct request *blk_pm_peek_request(
+ struct request_queue *q, struct request *rq)
+{
+ if (q->rpm_status == RPM_SUSPENDED ||
+ (q->rpm_status != RPM_ACTIVE && !(rq->cmd_flags & REQ_PM)))
+ return NULL;
+ else
+ return rq;
+}
+
+static inline void blk_pm_requeue_request(struct request *rq)
+{
+ if (!(rq->cmd_flags & REQ_PM))
+ rq->q->nr_pending--;
+}
+
+static inline void blk_pm_add_request(struct request_queue *q,
+ struct request *rq)
+{
+ if (!(rq->cmd_flags & REQ_PM) &&
+ q->nr_pending++ == 0 &&
+ (q->rpm_status == RPM_SUSPENDED ||
+ q->rpm_status == RPM_SUSPENDING))
+ pm_request_resume(q->dev);
+}
#else
static inline void blk_pm_runtime_init(struct request_queue *q,
struct device *dev) {}
@@ -984,6 +1018,13 @@ static inline int blk_pre_runtime_suspend(struct request_queue *q)
static inline void blk_post_runtime_suspend(struct request_queue *q, int err) {}
static inline void blk_pre_runtime_resume(struct request_queue *q) {}
static inline void blk_post_runtime_resume(struct request_queue *q, int err) {}
+
+static inline void blk_pm_put_request(struct request *rq) {}
+static inline struct request *blk_pm_peek_request(
+ struct request_queue *q, struct request *rq) { return rq; }
+static inline void blk_pm_requeue_request(struct request *rq) {}
+static inline void blk_pm_add_request(struct request_queue *q,
+ struct request *req) {}
#endif
/*
--
1.7.11.7
^ permalink raw reply related
* [PATCH v6 2/4] block: add runtime pm helpers
From: Aaron Lu @ 2013-01-06 8:41 UTC (permalink / raw)
To: Alan Stern, Jens Axboe, Rafael J. Wysocki, James Bottomley
Cc: linux-pm, linux-scsi, linux-kernel, Aaron Lu, Aaron Lu,
Shane Huang
In-Reply-To: <1357461697-4219-1-git-send-email-aaron.lu@intel.com>
From: Lin Ming <ming.m.lin@intel.com>
Add runtime pm helper functions:
void blk_pm_runtime_init(struct request_queue *q, struct device *dev)
- Initialization function for drivers to call.
int blk_pre_runtime_suspend(struct request_queue *q)
- If any requests are in the queue, return -EBUSY.
Otherwise set q->rpm_status to RPM_SUSPENDING and return 0.
void blk_post_runtime_suspend(struct request_queue *q, int err)
- If the suspend succeeded then set q->rpm_status to RPM_SUSPENDED.
Otherwise set it to RPM_ACTIVE.
void blk_pre_runtime_resume(struct request_queue *q)
- Set q->rpm_status to RPM_RESUMING.
void blk_post_runtime_resume(struct request_queue *q, int err)
- If the resume succeeded then set q->rpm_status to RPM_ACTIVE
and call __blk_run_queue.
Otherwise set q->rpm_status to RPM_SUSPENDED.
[aaron.lu@intel.com: do not touch last busy in these helper functions]
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Aaron Lu <aaron.lu@intel.com>
---
block/blk-core.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/blkdev.h | 28 +++++++++++++++++++++++++
2 files changed, 83 insertions(+)
diff --git a/block/blk-core.c b/block/blk-core.c
index c973249..6fc24bb 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -3055,6 +3055,61 @@ void blk_finish_plug(struct blk_plug *plug)
}
EXPORT_SYMBOL(blk_finish_plug);
+#ifdef CONFIG_PM_RUNTIME
+void blk_pm_runtime_init(struct request_queue *q, struct device *dev)
+{
+ q->dev = dev;
+ q->rpm_status = RPM_ACTIVE;
+ pm_runtime_use_autosuspend(q->dev);
+}
+EXPORT_SYMBOL(blk_pm_runtime_init);
+
+int blk_pre_runtime_suspend(struct request_queue *q)
+{
+ int ret = 0;
+
+ spin_lock_irq(q->queue_lock);
+ if (q->nr_pending)
+ ret = -EBUSY;
+ else
+ q->rpm_status = RPM_SUSPENDING;
+ spin_unlock_irq(q->queue_lock);
+ return ret;
+}
+EXPORT_SYMBOL(blk_pre_runtime_suspend);
+
+void blk_post_runtime_suspend(struct request_queue *q, int err)
+{
+ spin_lock_irq(q->queue_lock);
+ if (!err)
+ q->rpm_status = RPM_SUSPENDED;
+ else
+ q->rpm_status = RPM_ACTIVE;
+ spin_unlock_irq(q->queue_lock);
+}
+EXPORT_SYMBOL(blk_post_runtime_suspend);
+
+void blk_pre_runtime_resume(struct request_queue *q)
+{
+ spin_lock_irq(q->queue_lock);
+ q->rpm_status = RPM_RESUMING;
+ spin_unlock_irq(q->queue_lock);
+}
+EXPORT_SYMBOL(blk_pre_runtime_resume);
+
+void blk_post_runtime_resume(struct request_queue *q, int err)
+{
+ spin_lock_irq(q->queue_lock);
+ if (!err) {
+ q->rpm_status = RPM_ACTIVE;
+ __blk_run_queue(q);
+ } else
+ q->rpm_status = RPM_SUSPENDED;
+ spin_unlock_irq(q->queue_lock);
+}
+EXPORT_SYMBOL(blk_post_runtime_resume);
+#endif
+
int __init blk_dev_init(void)
{
BUILD_BUG_ON(__REQ_NR_BITS > 8 *
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index f94bc83..a96e144 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -19,6 +19,7 @@
#include <linux/gfp.h>
#include <linux/bsg.h>
#include <linux/smp.h>
+#include <linux/pm_runtime.h>
#include <asm/scatterlist.h>
@@ -360,6 +361,12 @@ struct request_queue {
*/
struct kobject kobj;
+#ifdef CONFIG_PM_RUNTIME
+ struct device *dev;
+ int rpm_status;
+ unsigned int nr_pending;
+#endif
+
/*
* queue settings
*/
@@ -959,6 +966,27 @@ struct request_queue *blk_alloc_queue_node(gfp_t, int);
extern void blk_put_queue(struct request_queue *);
/*
+ * block layer runtime pm functions
+ */
+#ifdef CONFIG_PM_RUNTIME
+extern void blk_pm_runtime_init(struct request_queue *q, struct device *dev);
+extern int blk_pre_runtime_suspend(struct request_queue *q);
+extern void blk_post_runtime_suspend(struct request_queue *q, int err);
+extern void blk_pre_runtime_resume(struct request_queue *q);
+extern void blk_post_runtime_resume(struct request_queue *q, int err);
+#else
+static inline void blk_pm_runtime_init(struct request_queue *q,
+ struct device *dev) {}
+static inline int blk_pre_runtime_suspend(struct request_queue *q)
+{
+ return -ENOSYS;
+}
+static inline void blk_post_runtime_suspend(struct request_queue *q, int err) {}
+static inline void blk_pre_runtime_resume(struct request_queue *q) {}
+static inline void blk_post_runtime_resume(struct request_queue *q, int err) {}
+#endif
+
+/*
* blk_plug permits building a queue of related requests by holding the I/O
* fragments for a short period. This allows merging of sequential requests
* into single larger request. As the requests are moved from a per-task list to
--
1.7.11.7
^ permalink raw reply related
* [PATCH v6 1/4] block: add a flag to identify PM request
From: Aaron Lu @ 2013-01-06 8:41 UTC (permalink / raw)
To: Alan Stern, Jens Axboe, Rafael J. Wysocki, James Bottomley
Cc: linux-pm, linux-scsi, linux-kernel, Aaron Lu, Aaron Lu,
Shane Huang
In-Reply-To: <1357461697-4219-1-git-send-email-aaron.lu@intel.com>
From: Lin Ming <ming.m.lin@intel.com>
Add a flag REQ_PM to identify the request is PM related.
As an example, modify scsi code to use this flag.
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Aaron Lu <aaron.lu@intel.com>
---
drivers/scsi/scsi_lib.c | 9 ++++-----
drivers/scsi/sd.c | 9 +++++----
include/linux/blk_types.h | 2 ++
include/scsi/scsi_device.h | 17 +++++++++++++----
4 files changed, 24 insertions(+), 13 deletions(-)
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index f1bf5af..af1b8b3 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -271,11 +271,10 @@ int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
}
EXPORT_SYMBOL(scsi_execute);
-
-int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd,
+int scsi_execute_req_flags(struct scsi_device *sdev, const unsigned char *cmd,
int data_direction, void *buffer, unsigned bufflen,
struct scsi_sense_hdr *sshdr, int timeout, int retries,
- int *resid)
+ int *resid, int flags)
{
char *sense = NULL;
int result;
@@ -286,14 +285,14 @@ int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd,
return DRIVER_ERROR << 24;
}
result = scsi_execute(sdev, cmd, data_direction, buffer, bufflen,
- sense, timeout, retries, 0, resid);
+ sense, timeout, retries, flags, resid);
if (sshdr)
scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, sshdr);
kfree(sense);
return result;
}
-EXPORT_SYMBOL(scsi_execute_req);
+EXPORT_SYMBOL(scsi_execute_req_flags);
/*
* Function: scsi_init_cmd_errh()
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 7992635..8ca160e 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -1424,8 +1424,9 @@ static int sd_sync_cache(struct scsi_disk *sdkp)
* Leave the rest of the command zero to indicate
* flush everything.
*/
- res = scsi_execute_req(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
- SD_FLUSH_TIMEOUT, SD_MAX_RETRIES, NULL);
+ res = scsi_execute_req_flags(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
+ SD_FLUSH_TIMEOUT, SD_MAX_RETRIES, NULL,
+ REQ_PM);
if (res == 0)
break;
}
@@ -3021,8 +3022,8 @@ static int sd_start_stop_device(struct scsi_disk *sdkp, int start)
if (!scsi_device_online(sdp))
return -ENODEV;
- res = scsi_execute_req(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
- SD_TIMEOUT, SD_MAX_RETRIES, NULL);
+ res = scsi_execute_req_flags(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
+ SD_TIMEOUT, SD_MAX_RETRIES, NULL, REQ_PM);
if (res) {
sd_printk(KERN_WARNING, sdkp, "START_STOP FAILED\n");
sd_print_result(sdkp, res);
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index cdf1119..fcc1ce2 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -175,6 +175,7 @@ enum rq_flag_bits {
__REQ_IO_STAT, /* account I/O stat */
__REQ_MIXED_MERGE, /* merge of different types, fail separately */
__REQ_KERNEL, /* direct IO to kernel pages */
+ __REQ_PM, /* runtime pm request */
__REQ_NR_BITS, /* stops here */
};
@@ -223,5 +224,6 @@ enum rq_flag_bits {
#define REQ_MIXED_MERGE (1 << __REQ_MIXED_MERGE)
#define REQ_SECURE (1 << __REQ_SECURE)
#define REQ_KERNEL (1 << __REQ_KERNEL)
+#define REQ_PM (1 << __REQ_PM)
#endif /* __LINUX_BLK_TYPES_H */
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index e65c62e..aff494c 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -393,10 +393,19 @@ extern int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
int data_direction, void *buffer, unsigned bufflen,
unsigned char *sense, int timeout, int retries,
int flag, int *resid);
-extern int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd,
- int data_direction, void *buffer, unsigned bufflen,
- struct scsi_sense_hdr *, int timeout, int retries,
- int *resid);
+extern int scsi_execute_req_flags(struct scsi_device *sdev,
+ const unsigned char *cmd, int data_direction, void *buffer,
+ unsigned bufflen, struct scsi_sense_hdr *sshdr, int timeout,
+ int retries, int *resid, int flags);
+
+static inline int scsi_execute_req(struct scsi_device *sdev,
+ const unsigned char *cmd, int data_direction, void *buffer,
+ unsigned bufflen, struct scsi_sense_hdr *sshdr, int timeout,
+ int retries, int *resid)
+{
+ return scsi_execute_req_flags(sdev, cmd, data_direction, buffer,
+ bufflen, sshdr, timeout, retries, resid, 0);
+}
#ifdef CONFIG_PM_RUNTIME
extern int scsi_autopm_get_device(struct scsi_device *);
--
1.7.11.7
^ permalink raw reply related
* [PATCH v6 0/4] block layer runtime pm
From: Aaron Lu @ 2013-01-06 8:41 UTC (permalink / raw)
To: Alan Stern, Jens Axboe, Rafael J. Wysocki, James Bottomley
Cc: linux-pm, linux-scsi, linux-kernel, Aaron Lu, Aaron Lu,
Shane Huang
In August 2010, Jens and Alan discussed about "Runtime PM and the block
layer". http://marc.info/?t=128259108400001&r=1&w=2
And then Alan has given a detailed implementation guide:
http://marc.info/?l=linux-scsi&m=133727953625963&w=2
To test:
# ls -l /sys/block/sda
/sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda
# echo 10000 > /sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/power/autosuspend_delay_ms
# echo auto > /sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/power/control
Then you'll see sda is suspended after 10secs idle.
[ 1767.680192] sd 2:0:0:0: [sda] Synchronizing SCSI cache
[ 1767.680317] sd 2:0:0:0: [sda] Stopping disk
And if you do some IO, it will resume immediately.
[ 1791.052438] sd 2:0:0:0: [sda] Starting disk
For test, I often set the autosuspend time to 1 second. If you are using
a GUI, the 10 seconds delay may be too long that the disk can not enter
runtime suspended state.
Note that sd's runtime suspend callback will dump some kernel messages
and the syslog daemon will write kernel message to /var/log/messages,
making the disk instantly resume after suspended. So for test, the
syslog daemon should better be temporarily stopped.
v6:
Take over from Lin Ming.
- Instead of put the device into autosuspend state in
blk_post_runtime_suspend, do it when the last request is finished.
This can also solve the problem illustrated below:
thread A thread B
|suspend timer expired |
| ... ... |a new request comes in,
| ... ... |blk_pm_add_request
| ... ... |skip request_resume due to
| ... ... |q->status is still RPM_ACTIVE
| rpm_suspend | ... ...
| scsi_runtime_suspend | ... ...
| blk_pre_runtime_suspend | ... ...
| return -EBUSY due to nr_pending | ... ...
| rpm_suspend done | ... ...
| | blk_pm_put_request, mark last busy
But no more trigger point, and the device will stay at RPM_ACTIVE state.
Run pm_runtime_autosuspend after the last request is finished solved
this problem.
- Requests which have the REQ_PM flag should not involve nr_pending
counting, or we may lose the condition to resume the device:
Suppose queue is active and nr_pending is 0. Then a REQ_PM request
comes and nr_pending will be increased to 1, but since the request has
REQ_PM flag, it will not cause resume. Before it is finished, a normal
request comes in, and since nr_pending is 1 now, it will not trigger
the resume of the device either. Bug.
- Do not quiesce the device in scsi bus level runtime suspend callback.
Since the only reason the device is to be runtime suspended is due to
no more requests pending for it, quiesce it is pointless.
- Remove scsi_autopm_* from sd_check_events as we are request driven.
- Call blk_pm_runtime_init in scsi_sysfs_initialize_dev, so that we do
not need to check queue's device in blk_pm_add/put_request.
- Do not mark last busy and initiate an autosuspend for the device in
blk_pm_runtime_init function.
- Do not mark last busy and initiate an autosuspend for the device in
block_post_runtime_resume, as when the request that triggered the
resume finished, the blk_pm_put_request will mark last busy and
initiate an autosuspend.
v5:
- rename scsi_execute_req to scsi_execute_req_flags
and wrap scsi_execute_req around it.
- add detail function descriptions in patch 2 log
- define static helper functions to do runtime pm work on block layer
and put the definitions inside a #ifdef block
v4:
- add CONFIG_PM_RUNTIME check
- update queue runtime pm status after system resume
- use pm_runtime_autosuspend instead of pm_request_autosuspend in scsi_runtime_idle
- always count PM request
v3:
- remove block layer suspend/resume callbacks
- add block layer runtime pm helper functions
v2:
- remove queue idle timer, use runtime pm core's auto suspend
Lin Ming (4):
block: add a flag to identify PM request
block: add runtime pm helpers
block: implement runtime pm strategy
sd: change to auto suspend mode
block/blk-core.c | 62 +++++++++++++++++++++++++++++++++++++++++
block/elevator.c | 4 +++
drivers/scsi/scsi_lib.c | 9 +++---
drivers/scsi/scsi_pm.c | 35 +++++++++++++++--------
drivers/scsi/scsi_sysfs.c | 1 +
drivers/scsi/sd.c | 21 ++++----------
include/linux/blk_types.h | 2 ++
include/linux/blkdev.h | 69 ++++++++++++++++++++++++++++++++++++++++++++++
include/scsi/scsi_device.h | 17 +++++++++---
9 files changed, 183 insertions(+), 37 deletions(-)
--
1.7.11.7
^ permalink raw reply
* [PATCH] power: max17040: use devm_kzalloc
From: Devendra Naga @ 2013-01-06 4:53 UTC (permalink / raw)
To: Anton Vorontsov, David Woodhouse, linux-pm; +Cc: Devendra Naga
use devm_kzalloc and no need of error path and unload frees
Signed-off-by: Devendra Naga <devendra.aaru@gmail.com>
---
drivers/power/max17040_battery.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/drivers/power/max17040_battery.c b/drivers/power/max17040_battery.c
index 22cfe9c..74a0bd9 100644
--- a/drivers/power/max17040_battery.c
+++ b/drivers/power/max17040_battery.c
@@ -207,7 +207,7 @@ static int max17040_probe(struct i2c_client *client,
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
return -EIO;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
@@ -225,7 +225,6 @@ static int max17040_probe(struct i2c_client *client,
ret = power_supply_register(&client->dev, &chip->battery);
if (ret) {
dev_err(&client->dev, "failed: power supply register\n");
- kfree(chip);
return ret;
}
@@ -244,7 +243,6 @@ static int max17040_remove(struct i2c_client *client)
power_supply_unregister(&chip->battery);
cancel_delayed_work(&chip->work);
- kfree(chip);
return 0;
}
--
1.7.10.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox