From patchwork Wed Dec 21 11:59:56 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Amit Daniel Kachhap X-Patchwork-Id: 5928 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id 25BEA242F7 for ; Wed, 21 Dec 2011 12:00:57 +0000 (UTC) Received: from mail-ey0-f180.google.com (mail-ey0-f180.google.com [209.85.215.180]) by fiordland.canonical.com (Postfix) with ESMTP id 18BF5A18485 for ; Wed, 21 Dec 2011 12:00:57 +0000 (UTC) Received: by eaac11 with SMTP id c11so4457384eaa.11 for ; Wed, 21 Dec 2011 04:00:57 -0800 (PST) Received: by 10.205.120.148 with SMTP id fy20mr1668519bkc.125.1324468856825; Wed, 21 Dec 2011 04:00:56 -0800 (PST) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.205.82.144 with SMTP id ac16cs36491bkc; Wed, 21 Dec 2011 04:00:56 -0800 (PST) Received: by 10.236.156.7 with SMTP id l7mr8961729yhk.60.1324468854184; Wed, 21 Dec 2011 04:00:54 -0800 (PST) Received: from mail-gy0-f178.google.com (mail-gy0-f178.google.com [209.85.160.178]) by mx.google.com with ESMTPS id t29si2539495ano.24.2011.12.21.04.00.53 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 21 Dec 2011 04:00:54 -0800 (PST) Received-SPF: pass (google.com: domain of amitdanielk@gmail.com designates 209.85.160.178 as permitted sender) client-ip=209.85.160.178; Authentication-Results: mx.google.com; spf=pass (google.com: domain of amitdanielk@gmail.com designates 209.85.160.178 as permitted sender) smtp.mail=amitdanielk@gmail.com; dkim=pass (test mode) header.i=@gmail.com Received: by mail-gy0-f178.google.com with SMTP id g21so5143411ghb.37 for ; Wed, 21 Dec 2011 04:00:53 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=sender:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references; bh=Wk6O6wjoaYKBsCHHFuG7wvtAeyvCjVpUfC2a+y2x+ho=; b=vcHLVeiC4U7lufSPXxfsqNDPByDJPFApc+XdoMJR02+0bQeKvQLBXgnfKoRkm+0i/9 iZfQzwbuOdSoS0J2DGBaVVq4Prc2g9WY8r0XA/f29eZC3K/IJIZTwcaFOxcCH9K07vdT 9W2hpDwCSO6XccAHk2n7dLGoijIRnhMMtC0l0= Received: by 10.50.47.136 with SMTP id d8mr2916857ign.21.1324468853429; Wed, 21 Dec 2011 04:00:53 -0800 (PST) Received: from localhost.localdomain ([115.113.119.130]) by mx.google.com with ESMTPS id t5sm18201763igb.4.2011.12.21.04.00.48 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 21 Dec 2011 04:00:52 -0800 (PST) Sender: amit kachhap From: Amit Daniel Kachhap To: linux-pm@lists.linux-foundation.org, linux-samsung-soc@vger.kernel.org Cc: linux-kernel@vger.kernel.org, mjg59@srcf.ucam.org, linux-acpi@vger.kernel.org, lenb@kernel.org, linaro-dev@lists.linaro.org, lm-sensors@lm-sensors.org, amit.kachhap@linaro.org, patches@linaro.org Subject: [RFC PATCH 1/3] thermal: exynos: Add thermal interface support for linux thermal layer Date: Wed, 21 Dec 2011 17:29:56 +0530 Message-Id: <1324468798-20423-2-git-send-email-amit.kachhap@linaro.org> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1324468798-20423-1-git-send-email-amit.kachhap@linaro.org> References: <1324468798-20423-1-git-send-email-amit.kachhap@linaro.org> This codes uses the generic linux thermal layer and creates a bridge between temperature sensors, linux thermal framework and cooling devices for samsung exynos platform. This layer recieves or monitor the temperature from the sensor and informs the generic thermal layer to take the necessary cooling action. Signed-off-by: Amit Daniel Kachhap --- drivers/thermal/Kconfig | 8 ++ drivers/thermal/Makefile | 1 + drivers/thermal/exynos_thermal.c | 255 ++++++++++++++++++++++++++++++++++++++ include/linux/exynos_thermal.h | 59 +++++++++ 4 files changed, 323 insertions(+), 0 deletions(-) create mode 100644 drivers/thermal/exynos_thermal.c create mode 100644 include/linux/exynos_thermal.h diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 298c1cd..4e8df56 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -29,3 +29,11 @@ config CPU_THERMAL This will be useful for platforms using the generic thermal interface and not the ACPI interface. If you want this support, you should say Y or M here. + +config SAMSUNG_THERMAL_INTERFACE + bool "Samsung Thermal interface support" + depends on THERMAL && CPU_THERMAL + help + This is a samsung thermal interface which will be used as + a link between sensors and cooling devices with linux thermal + framework. diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 655cbc4..c67b6b2 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_THERMAL) += thermal_sys.o obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o +obj-$(CONFIG_SAMSUNG_THERMAL_INTERFACE) += exynos_thermal.o diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c new file mode 100644 index 0000000..7c916db --- /dev/null +++ b/drivers/thermal/exynos_thermal.c @@ -0,0 +1,255 @@ +/* linux/drivers/thermal/exynos_thermal.c + * + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct exynos4_thermal_zone { + unsigned int idle_interval; + unsigned int active_interval; + struct thermal_zone_device *therm_dev; + struct thermal_cooling_device *cool_dev; + struct platform_device *exynos4_dev; + struct thermal_sensor_conf *sensor_conf; + struct exynos4_tmu_platform_data *sensor_data; +}; + +static struct exynos4_thermal_zone *th_zone; + +static int exynos4_get_mode(struct thermal_zone_device *thermal, + enum thermal_device_mode *mode) +{ + if (th_zone->sensor_conf) { + pr_info("Temperature sensor not initialised\n"); + *mode = THERMAL_DEVICE_DISABLED; + } else + *mode = THERMAL_DEVICE_ENABLED; + return 0; +} + +static int exynos4_set_mode(struct thermal_zone_device *thermal, + enum thermal_device_mode mode) +{ + if (!th_zone->therm_dev) { + pr_notice("thermal zone not registered\n"); + return 0; + } + if (mode == THERMAL_DEVICE_ENABLED) + th_zone->therm_dev->polling_delay = + th_zone->active_interval*1000; + else + th_zone->therm_dev->polling_delay = + th_zone->idle_interval*1000; + + thermal_zone_device_update(th_zone->therm_dev); + pr_info("thermal polling set for duration=%d sec\n", + th_zone->therm_dev->polling_delay/1000); + return 0; +} + +/*This may be called from interrupt based temperature sensor*/ +void exynos4_report_trigger(void) +{ + unsigned int th_temp = th_zone->sensor_data->threshold; + unsigned int monitor_temp = th_temp + + th_zone->sensor_data->trigger_levels[1]; + + thermal_zone_device_update(th_zone->therm_dev); + + if (th_zone->therm_dev->last_temperature > monitor_temp) + th_zone->therm_dev->polling_delay = + th_zone->active_interval*1000; + else + th_zone->therm_dev->polling_delay = + th_zone->idle_interval*1000; +} + +static int exynos4_get_trip_type(struct thermal_zone_device *thermal, int trip, + enum thermal_trip_type *type) +{ + if (trip == 0 || trip == 1) + *type = THERMAL_TRIP_STATE_ACTIVE; + else if (trip == 2) + *type = THERMAL_TRIP_CRITICAL; + else + return -EINVAL; + + return 0; +} + +static int exynos4_get_trip_temp(struct thermal_zone_device *thermal, int trip, + unsigned long *temp) +{ + unsigned int th_temp = th_zone->sensor_data->threshold; + + /*Monitor zone*/ + if (trip == 0) + *temp = th_temp + th_zone->sensor_data->trigger_levels[1]; + /*Warn zone*/ + else if (trip == 1) + *temp = th_temp + th_zone->sensor_data->trigger_levels[2]; + /*Panic zone*/ + else if (trip == 2) + *temp = th_temp + th_zone->sensor_data->trigger_levels[3]; + else + return -EINVAL; + /*convert the temperature into millicelsius*/ + *temp = *temp * 1000; + return 0; +} + +static int exynos4_get_crit_temp(struct thermal_zone_device *thermal, + unsigned long *temp) +{ + unsigned int th_temp = th_zone->sensor_data->threshold; + /*Panic zone*/ + *temp = th_temp + th_zone->sensor_data->trigger_levels[3]; + /*convert the temperature into millicelsius*/ + *temp = *temp * 1000; + return 0; +} + +static int exynos4_bind(struct thermal_zone_device *thermal, + struct thermal_cooling_device *cdev) +{ + /* if the cooling device is the one from exynos4 bind it */ + if (cdev != th_zone->cool_dev) + return 0; + + if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) { + pr_err("error binding cooling dev\n"); + return -EINVAL; + } + if (thermal_zone_bind_cooling_device(thermal, 1, cdev)) { + pr_err("error binding cooling dev\n"); + return -EINVAL; + } + + return 0; +} + +static int exynos4_unbind(struct thermal_zone_device *thermal, + struct thermal_cooling_device *cdev) +{ + if (cdev != th_zone->cool_dev) + return 0; + if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) { + pr_err("error unbinding cooling dev\n"); + return -EINVAL; + } + return 0; +} + +static int exynos4_get_temp(struct thermal_zone_device *thermal, + unsigned long *temp) +{ + void *data; + + if (!th_zone->sensor_conf) { + pr_info("Temperature sensor not initialised\n"); + return -EINVAL; + } + data = th_zone->sensor_conf->private_data; + *temp = th_zone->sensor_conf->read_temperature(data); + /*convert the temperature into millicelsius*/ + *temp = *temp * 1000; + return 0; +} + +/* bind callback functions to thermalzone */ +static struct thermal_zone_device_ops exynos4_dev_ops = { + .bind = exynos4_bind, + .unbind = exynos4_unbind, + .get_temp = exynos4_get_temp, + .get_mode = exynos4_get_mode, + .set_mode = exynos4_set_mode, + .get_trip_type = exynos4_get_trip_type, + .get_trip_temp = exynos4_get_trip_temp, + .get_crit_temp = exynos4_get_crit_temp, +}; + +int exynos4_register_thermal(struct thermal_sensor_conf *sensor_conf) +{ + int ret; + + if (!sensor_conf) { + pr_err("Temperature sensor not initialised\n"); + return -EINVAL; + } + + th_zone = kzalloc(sizeof(struct exynos4_thermal_zone), GFP_KERNEL); + if (!th_zone) { + ret = -ENOMEM; + goto err_unregister; + } + + th_zone->sensor_conf = sensor_conf; + + th_zone->sensor_data = sensor_conf->sensor_data; + if (!th_zone->sensor_data) { + pr_err("Temperature sensor data not initialised\n"); + ret = -EINVAL; + goto err_unregister; + } + + th_zone->cool_dev = cpufreq_cooling_register( + (struct freq_pctg_table *)th_zone->sensor_data->freq_tab, + th_zone->sensor_data->freq_tab_count, cpumask_of(0)); + + if (IS_ERR(th_zone->cool_dev)) { + pr_err("Failed to register cpufreq cooling device\n"); + ret = -EINVAL; + goto err_unregister; + } + + th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name, + 3, NULL, &exynos4_dev_ops, 0, 0, 0, 1000); + if (IS_ERR(th_zone->therm_dev)) { + pr_err("Failed to register thermal zone device\n"); + ret = -EINVAL; + goto err_unregister; + } + + th_zone->active_interval = 1; + th_zone->idle_interval = 10; + + exynos4_set_mode(th_zone->therm_dev, THERMAL_DEVICE_DISABLED); + + pr_info("Exynos: Kernel Thermal management registered\n"); + + return 0; + +err_unregister: + exynos4_unregister_thermal(); + return ret; +} +EXPORT_SYMBOL(exynos4_register_thermal); + +void exynos4_unregister_thermal(void) +{ + if (th_zone && th_zone->cool_dev) + cpufreq_cooling_unregister(); + + if (th_zone && th_zone->therm_dev) + thermal_zone_device_unregister(th_zone->therm_dev); + + kfree(th_zone); + + pr_info("Exynos: Kernel Thermal management unregistered\n"); +} +EXPORT_SYMBOL(exynos4_unregister_thermal); diff --git a/include/linux/exynos_thermal.h b/include/linux/exynos_thermal.h new file mode 100644 index 0000000..de7195b --- /dev/null +++ b/include/linux/exynos_thermal.h @@ -0,0 +1,59 @@ +/* linux/include/linux/exynos_thermal.h + * + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef THERMAL_INTERFACE_H +#define THERMAL_INTERFACE_H +/* CPU Zone information */ + +#define SENSOR_NAME_LEN 16 + +#define PANIC_ZONE 4 +#define WARN_ZONE 3 +#define MONITOR_ZONE 2 +#define SAFE_ZONE 1 +#define NO_ACTION 0 + +/** + * struct exynos4_tmu_platform_data + * @name: name of the temperature sensor + * @read_temperature: A function pointer to read temperature info + * @private_data: Temperature sensor private data + * @sensor_data: Sensor specific information like trigger temperature, level + */ +struct thermal_sensor_conf { + char name[SENSOR_NAME_LEN]; + int (*read_temperature)(void *data); + void *private_data; + void *sensor_data; +}; + +/** + * exynos4_register_thermal: Register to the exynos thermal interface. + * @sensor_conf: Structure containing temperature sensor information + * + * returns zero on success, else negative errno. + */ +int exynos4_register_thermal(struct thermal_sensor_conf *sensor_conf); + +/** + * exynos4_unregister_thermal: Un-register from the exynos thermal interface. + * + * return not applicable. + */ +void exynos4_unregister_thermal(void); + +/** + * exynos4_report_trigger: Report any trigger level crossed in the + * temperature sensor. This may be useful to take any cooling action. + * + * return not applicable. + */ +extern void exynos4_report_trigger(void); +#endif