From patchwork Thu Aug 1 16:33:12 2013
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: Philipp Zabel
X-Patchwork-Id: 2837147
X-Patchwork-Delegate: rui.zhang@intel.com
Return-Path:
X-Original-To: patchwork-linux-pm@patchwork.kernel.org
Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org
Received: from mail.kernel.org (mail.kernel.org [198.145.19.201])
by patchwork2.web.kernel.org (Postfix) with ESMTP id 9FCA0C0319
for ;
Thu, 1 Aug 2013 16:33:47 +0000 (UTC)
Received: from mail.kernel.org (localhost [127.0.0.1])
by mail.kernel.org (Postfix) with ESMTP id 3E5AA20357
for ;
Thu, 1 Aug 2013 16:33:45 +0000 (UTC)
Received: from vger.kernel.org (vger.kernel.org [209.132.180.67])
by mail.kernel.org (Postfix) with ESMTP id D099720350
for ;
Thu, 1 Aug 2013 16:33:43 +0000 (UTC)
Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand
id S1753943Ab3HAQdn (ORCPT
);
Thu, 1 Aug 2013 12:33:43 -0400
Received: from metis.ext.pengutronix.de ([92.198.50.35]:59816 "EHLO
metis.ext.pengutronix.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org
with ESMTP id S1752842Ab3HAQdm (ORCPT
); Thu, 1 Aug 2013 12:33:42 -0400
Received: from dude.hi.pengutronix.de ([10.1.0.7] helo=dude.pengutronix.de)
by metis.ext.pengutronix.de with esmtp (Exim 4.72)
(envelope-from )
id 1V4voV-0000C0-Ke; Thu, 01 Aug 2013 18:33:19 +0200
From: Philipp Zabel
To: linux-pm@vger.kernel.org
Cc: Shawn Guo , Zhang Rui ,
Eduardo Valentin ,
kernel@pengutronix.de, Philipp Zabel
Subject: [PATCH 2/2] thermal: imx: implement thermal alarm interrupt handling
Date: Thu, 1 Aug 2013 18:33:12 +0200
Message-Id: <1375374792-32326-3-git-send-email-p.zabel@pengutronix.de>
X-Mailer: git-send-email 1.8.4.rc0
In-Reply-To: <1375374792-32326-1-git-send-email-p.zabel@pengutronix.de>
References: <1375374792-32326-1-git-send-email-p.zabel@pengutronix.de>
X-SA-Exim-Connect-IP: 10.1.0.7
X-SA-Exim-Mail-From: p.zabel@pengutronix.de
X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de);
SAEximRunCond expanded to false
X-PTX-Original-Recipient: linux-pm@vger.kernel.org
Sender: linux-pm-owner@vger.kernel.org
Precedence: bulk
List-ID:
X-Mailing-List: linux-pm@vger.kernel.org
X-Spam-Status: No, score=-8.4 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI,
RP_MATCHES_RCVD,
UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1
X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org
X-Virus-Scanned: ClamAV using ClamSMTP
Enable automatic measurements at 10 Hz and use the alarm interrupt to react
more quickly to sudden temperature changes above the passive or critical
temperature trip points.
Signed-off-by: Philipp Zabel
Acked-by: Shawn Guo
---
drivers/thermal/imx_thermal.c | 140 ++++++++++++++++++++++++++++++++++++++----
1 file changed, 127 insertions(+), 13 deletions(-)
diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c
index 9387e47..411be0a 100644
--- a/drivers/thermal/imx_thermal.c
+++ b/drivers/thermal/imx_thermal.c
@@ -12,6 +12,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -31,6 +32,8 @@
#define MISC0_REFTOP_SELBIASOFF (1 << 3)
#define TEMPSENSE0 0x0180
+#define TEMPSENSE0_ALARM_VALUE_SHIFT 20
+#define TEMPSENSE0_ALARM_VALUE_MASK (0xfff << TEMPSENSE0_ALARM_VALUE_SHIFT)
#define TEMPSENSE0_TEMP_CNT_SHIFT 8
#define TEMPSENSE0_TEMP_CNT_MASK (0xfff << TEMPSENSE0_TEMP_CNT_SHIFT)
#define TEMPSENSE0_FINISHED (1 << 2)
@@ -66,33 +69,62 @@ struct imx_thermal_data {
int c1, c2; /* See formula in imx_get_sensor_data() */
unsigned long temp_passive;
unsigned long temp_critical;
+ unsigned long alarm_temp;
+ unsigned long last_temp;
+ bool irq_enabled;
+ int irq;
};
+static void imx_set_alarm_temp(struct imx_thermal_data *data,
+ signed long alarm_temp)
+{
+ struct regmap *map = data->tempmon;
+ int alarm_value;
+
+ data->alarm_temp = alarm_temp;
+ alarm_value = (alarm_temp - data->c2) / data->c1;
+ regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_ALARM_VALUE_MASK);
+ regmap_write(map, TEMPSENSE0 + REG_SET, alarm_value <<
+ TEMPSENSE0_ALARM_VALUE_SHIFT);
+}
+
static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp)
{
struct imx_thermal_data *data = tz->devdata;
struct regmap *map = data->tempmon;
- static unsigned long last_temp;
unsigned int n_meas;
+ bool wait;
u32 val;
- /*
- * Every time we measure the temperature, we will power on the
- * temperature sensor, enable measurements, take a reading,
- * disable measurements, power off the temperature sensor.
- */
- regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
- regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
+ if (data->mode == THERMAL_DEVICE_ENABLED) {
+ /* Check if a measurement is currently in progress */
+ regmap_read(map, TEMPSENSE0, &val);
+ wait = !(val & TEMPSENSE0_FINISHED);
+ } else {
+ /*
+ * Every time we measure the temperature, we will power on the
+ * temperature sensor, enable measurements, take a reading,
+ * disable measurements, power off the temperature sensor.
+ */
+ regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
+ regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
+
+ wait = true;
+ }
/*
* According to the temp sensor designers, it may require up to ~17us
* to complete a measurement.
*/
- usleep_range(20, 50);
+ if (wait)
+ usleep_range(20, 50);
regmap_read(map, TEMPSENSE0, &val);
- regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP);
- regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
+
+ if (data->mode != THERMAL_DEVICE_ENABLED) {
+ regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP);
+ regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
+ }
if ((val & TEMPSENSE0_FINISHED) == 0) {
dev_dbg(&tz->device, "temp measurement never finished\n");
@@ -104,9 +136,24 @@ static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp)
/* See imx_get_sensor_data() for formula derivation */
*temp = data->c2 + data->c1 * n_meas;
- if (*temp != last_temp) {
+ /* Update alarm value to next higher trip point */
+ if (data->alarm_temp == data->temp_passive && *temp >= data->temp_passive)
+ imx_set_alarm_temp(data, data->temp_critical);
+ if (data->alarm_temp == data->temp_critical && *temp < data->temp_passive) {
+ imx_set_alarm_temp(data, data->temp_passive);
+ dev_dbg(&tz->device, "thermal alarm off: T < %lu\n",
+ data->alarm_temp / 1000);
+ }
+
+ if (*temp != data->last_temp) {
dev_dbg(&tz->device, "millicelsius: %ld\n", *temp);
- last_temp = *temp;
+ data->last_temp = *temp;
+ }
+
+ /* Reenable alarm IRQ if temperature below alarm temperature */
+ if (!data->irq_enabled && *temp < data->alarm_temp) {
+ data->irq_enabled = true;
+ enable_irq(data->irq);
}
return 0;
@@ -126,13 +173,30 @@ static int imx_set_mode(struct thermal_zone_device *tz,
enum thermal_device_mode mode)
{
struct imx_thermal_data *data = tz->devdata;
+ struct regmap *map = data->tempmon;
if (mode == THERMAL_DEVICE_ENABLED) {
tz->polling_delay = IMX_POLLING_DELAY;
tz->passive_delay = IMX_PASSIVE_DELAY;
+
+ regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
+ regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
+
+ if (!data->irq_enabled) {
+ data->irq_enabled = true;
+ enable_irq(data->irq);
+ }
} else {
+ regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP);
+ regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
+
tz->polling_delay = 0;
tz->passive_delay = 0;
+
+ if (data->irq_enabled) {
+ disable_irq(data->irq);
+ data->irq_enabled = false;
+ }
}
data->mode = mode;
@@ -181,6 +245,8 @@ static int imx_set_trip_temp(struct thermal_zone_device *tz, int trip,
data->temp_passive = temp;
+ imx_set_alarm_temp(data, temp);
+
return 0;
}
@@ -299,11 +365,34 @@ static int imx_get_sensor_data(struct platform_device *pdev)
return 0;
}
+static irqreturn_t imx_thermal_alarm_irq(int irq, void *dev)
+{
+ struct imx_thermal_data *data = dev;
+
+ disable_irq_nosync(irq);
+ data->irq_enabled = false;
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t imx_thermal_alarm_irq_thread(int irq, void *dev)
+{
+ struct imx_thermal_data *data = dev;
+
+ dev_dbg(&data->tz->device, "THERMAL ALARM: T > %lu\n",
+ data->alarm_temp / 1000);
+
+ thermal_zone_device_update(data->tz);
+
+ return IRQ_HANDLED;
+}
+
static int imx_thermal_probe(struct platform_device *pdev)
{
struct imx_thermal_data *data;
struct cpumask clip_cpus;
struct regmap *map;
+ int measure_freq;
int ret;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
@@ -318,6 +407,18 @@ static int imx_thermal_probe(struct platform_device *pdev)
}
data->tempmon = map;
+ data->irq = platform_get_irq(pdev, 0);
+ if (data->irq < 0)
+ return data->irq;
+
+ ret = devm_request_threaded_irq(&pdev->dev, data->irq,
+ imx_thermal_alarm_irq, imx_thermal_alarm_irq_thread,
+ 0, "imx_thermal", data);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret);
+ return ret;
+ }
+
platform_set_drvdata(pdev, data);
ret = imx_get_sensor_data(pdev);
@@ -356,6 +457,15 @@ static int imx_thermal_probe(struct platform_device *pdev)
return ret;
}
+ /* Enable measurements at ~ 10 Hz */
+ regmap_write(map, TEMPSENSE1 + REG_CLR, TEMPSENSE1_MEASURE_FREQ);
+ measure_freq = DIV_ROUND_UP(32768, 10); /* 10 Hz */
+ regmap_write(map, TEMPSENSE1 + REG_SET, measure_freq);
+ imx_set_alarm_temp(data, data->temp_passive);
+ regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
+ regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
+
+ data->irq_enabled = true;
data->mode = THERMAL_DEVICE_ENABLED;
return 0;
@@ -364,6 +474,10 @@ static int imx_thermal_probe(struct platform_device *pdev)
static int imx_thermal_remove(struct platform_device *pdev)
{
struct imx_thermal_data *data = platform_get_drvdata(pdev);
+ struct regmap *map = data->tempmon;
+
+ /* Disable measurements */
+ regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
thermal_zone_device_unregister(data->tz);
cpufreq_cooling_unregister(data->cdev);