The interrupt for the temperature threshold is not enabled at the end of the
probe function, enable it after the setup is complete.
On the other side, the irq_enabled is not correctly set as we are checking if
the interrupt is masked where 'yes' means irq_enabled=false.
irq_get_irqchip_state(data->irq, IRQCHIP_STATE_MASKED,
&data->irq_enabled);
As we are always enabling the interrupt, it is pointless to check if
the interrupt is masked or not, just set irq_enabled to 'true'.
Signed-off-by: Daniel Lezcano <[email protected]>
Reviewed-by: Leo Yan <[email protected]>
Tested-by: Leo Yan <[email protected]>
---
drivers/thermal/hisi_thermal.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c
index bd3572c..8381696 100644
--- a/drivers/thermal/hisi_thermal.c
+++ b/drivers/thermal/hisi_thermal.c
@@ -345,8 +345,7 @@ static int hisi_thermal_probe(struct platform_device *pdev)
}
hisi_thermal_enable_bind_irq_sensor(data);
- irq_get_irqchip_state(data->irq, IRQCHIP_STATE_MASKED,
- &data->irq_enabled);
+ data->irq_enabled = true;
for (i = 0; i < HISI_MAX_SENSORS; ++i) {
ret = hisi_thermal_register_sensor(pdev, data,
@@ -358,6 +357,8 @@ static int hisi_thermal_probe(struct platform_device *pdev)
hisi_thermal_toggle_sensor(&data->sensors[i], true);
}
+ enable_irq(data->irq);
+
return 0;
}
--
2.7.4
The threaded interrupt for the alarm interrupt is requested before the
temperature controller is setup. This one can fire an interrupt immediately
leading to a kernel panic as the sensor data is not initialized.
In order to prevent that, move the threaded irq after the Tsensor is setup.
Signed-off-by: Daniel Lezcano <[email protected]>
Reviewed-by: Leo Yan <[email protected]>
Tested-by: Leo Yan <[email protected]>
---
drivers/thermal/hisi_thermal.c | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c
index 725d0d4..f69aea0 100644
--- a/drivers/thermal/hisi_thermal.c
+++ b/drivers/thermal/hisi_thermal.c
@@ -287,15 +287,6 @@ static int hisi_thermal_probe(struct platform_device *pdev)
if (data->irq < 0)
return data->irq;
- ret = devm_request_threaded_irq(&pdev->dev, data->irq,
- hisi_thermal_alarm_irq,
- hisi_thermal_alarm_irq_thread,
- 0, "hisi_thermal", data);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret);
- return ret;
- }
-
platform_set_drvdata(pdev, data);
data->clk = devm_clk_get(&pdev->dev, "thermal_clk");
@@ -328,6 +319,15 @@ static int hisi_thermal_probe(struct platform_device *pdev)
hisi_thermal_toggle_sensor(&data->sensors, true);
+ ret = devm_request_threaded_irq(&pdev->dev, data->irq,
+ hisi_thermal_alarm_irq,
+ hisi_thermal_alarm_irq_thread,
+ 0, "hisi_thermal", data);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret);
+ return ret;
+ }
+
enable_irq(data->irq);
return 0;
--
2.7.4
The DT specifies a threshold of 65000, we setup the register with a value in
the temperature resolution for the controller, 64656.
When we reach 64656, the interrupt fires, the interrupt is disabled. Then the
irq thread runs and calls thermal_zone_device_update() which will call in turn
hisi_thermal_get_temp().
The function will look if the temperature decreased, assuming it was more than
65000, but that is not the case because the current temperature is 64656
(because of the rounding when setting the threshold). This condition being
true, we re-enable the interrupt which fires immediately after exiting the irq
thread. That happens again and again until the temperature goes to more than
65000.
Potentially, there is here an interrupt storm if the temperature stabilizes at
this temperature. A very unlikely case but possible.
In any case, it does not make sense to handle dozens of alarm interrupt for
nothing.
Fix this by rounding the threshold value to the controller resolution so the
check against the threshold is consistent with the one set in the controller.
Signed-off-by: Daniel Lezcano <[email protected]>
Reviewed-by: Leo Yan <[email protected]>
Tested-by: Leo Yan <[email protected]>
---
drivers/thermal/hisi_thermal.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c
index 583bc19..f523197 100644
--- a/drivers/thermal/hisi_thermal.c
+++ b/drivers/thermal/hisi_thermal.c
@@ -90,6 +90,12 @@ static inline long hisi_thermal_temp_to_step(long temp)
return (temp - HISI_TEMP_BASE) / HISI_TEMP_STEP;
}
+static inline long hisi_thermal_round_temp(int temp)
+{
+ return hisi_thermal_step_to_temp(
+ hisi_thermal_temp_to_step(temp));
+}
+
static long hisi_thermal_get_sensor_temp(struct hisi_thermal_data *data,
struct hisi_thermal_sensor *sensor)
{
@@ -221,7 +227,7 @@ static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev)
sensor = &data->sensors;
dev_crit(&data->pdev->dev, "THERMAL ALARM: T > %d\n",
- sensor->thres_temp / 1000);
+ sensor->thres_temp);
mutex_unlock(&data->thermal_lock);
thermal_zone_device_update(data->sensors.tzd,
@@ -255,7 +261,7 @@ static int hisi_thermal_register_sensor(struct platform_device *pdev,
for (i = 0; i < of_thermal_get_ntrips(sensor->tzd); i++) {
if (trip[i].type == THERMAL_TRIP_PASSIVE) {
- sensor->thres_temp = trip[i].temperature;
+ sensor->thres_temp = hisi_thermal_round_temp(trip[i].temperature);
break;
}
}
--
2.7.4
The threaded interrupt inspect the sensors structure to look in the temp
threshold field, but this field is read-only in all the code, except in the
probe function before the threaded interrupt is set. In other words there
is not race window in the threaded interrupt when reading the field value.
Signed-off-by: Daniel Lezcano <[email protected]>
Reviewed-by: Leo Yan <[email protected]>
---
drivers/thermal/hisi_thermal.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c
index f523197..9ec5f29 100644
--- a/drivers/thermal/hisi_thermal.c
+++ b/drivers/thermal/hisi_thermal.c
@@ -221,14 +221,10 @@ static irqreturn_t hisi_thermal_alarm_irq(int irq, void *dev)
static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev)
{
struct hisi_thermal_data *data = dev;
- struct hisi_thermal_sensor *sensor;
-
- mutex_lock(&data->thermal_lock);
- sensor = &data->sensors;
+ struct hisi_thermal_sensor *sensor = &data->sensors;
dev_crit(&data->pdev->dev, "THERMAL ALARM: T > %d\n",
sensor->thres_temp);
- mutex_unlock(&data->thermal_lock);
thermal_zone_device_update(data->sensors.tzd,
THERMAL_EVENT_UNSPECIFIED);
--
2.7.4
The sensor is all setup, bind, resetted, acked, etc... every single second.
That was the way to workaround a problem with the interrupt bouncing again and
again.
With the following changes, we fix all in one:
- Do the setup, one time, at probe time
- Add the IRQF_ONESHOT, ack the interrupt in the threaded handler
- Remove the interrupt handler
- Set the correct value for the LAG register
- Remove all the irq_enabled stuff in the code as the interruption
handling is fixed
- Remove the 3ms delay
- Reorder the initialization routine to be in the right order
It ends up to a nicer code and more efficient, the 3-5ms delay is removed from
the get_temp() path.
Signed-off-by: Daniel Lezcano <[email protected]>
Reviewed-by: Leo Yan <[email protected]>
Tested-by: Leo Yan <[email protected]>
---
drivers/thermal/hisi_thermal.c | 203 +++++++++++++++++++----------------------
1 file changed, 93 insertions(+), 110 deletions(-)
diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c
index 10ef6bc..3ce9a4f 100644
--- a/drivers/thermal/hisi_thermal.c
+++ b/drivers/thermal/hisi_thermal.c
@@ -41,6 +41,7 @@
#define HISI_TEMP_BASE (-60000)
#define HISI_TEMP_RESET (100000)
#define HISI_TEMP_STEP (784)
+#define HISI_TEMP_LAG (3500)
#define HISI_MAX_SENSORS 4
#define HISI_DEFAULT_SENSOR 2
@@ -60,8 +61,6 @@ struct hisi_thermal_data {
struct clk *clk;
struct hisi_thermal_sensor sensors;
int irq;
- bool irq_enabled;
-
void __iomem *regs;
};
@@ -99,9 +98,40 @@ static inline long hisi_thermal_round_temp(int temp)
hisi_thermal_temp_to_step(temp));
}
+/*
+ * The lag register contains 5 bits encoding the temperature in steps.
+ *
+ * Each time the temperature crosses the threshold boundary, an
+ * interrupt is raised. It could be when the temperature is going
+ * above the threshold or below. However, if the temperature is
+ * fluctuating around this value due to the load, we can receive
+ * several interrupts which may not desired.
+ *
+ * We can setup a temperature representing the delta between the
+ * threshold and the current temperature when the temperature is
+ * decreasing.
+ *
+ * For instance: the lag register is 5°C, the threshold is 65°C, when
+ * the temperature reaches 65°C an interrupt is raised and when the
+ * temperature decrease to 65°C - 5°C another interrupt is raised.
+ *
+ * A very short lag can lead to an interrupt storm, a long lag
+ * increase the latency to react to the temperature changes. In our
+ * case, that is not really a problem as we are polling the
+ * temperature.
+ *
+ * [0:4] : lag register
+ *
+ * The temperature is coded in steps, cf. HISI_TEMP_STEP.
+ *
+ * Min : 0x00 : 0.0 °C
+ * Max : 0x1F : 24.3 °C
+ *
+ * The 'value' parameter is in milliCelsius.
+ */
static inline void hisi_thermal_set_lag(void __iomem *addr, int value)
{
- writel(value, addr + TEMP0_LAG);
+ writel((value / HISI_TEMP_STEP) & 0x1F, addr + TEMP0_LAG);
}
static inline void hisi_thermal_alarm_clear(void __iomem *addr, int value)
@@ -171,71 +201,6 @@ static inline void hisi_thermal_hdak_set(void __iomem *addr, int value)
(value << 4), addr + TEMP0_CFG);
}
-static long hisi_thermal_get_sensor_temp(struct hisi_thermal_data *data,
- struct hisi_thermal_sensor *sensor)
-{
- long val;
-
- mutex_lock(&data->thermal_lock);
-
- /* disable interrupt */
- hisi_thermal_alarm_enable(data->regs, 0);
- hisi_thermal_alarm_clear(data->regs, 1);
-
- /* disable module firstly */
- hisi_thermal_enable(data->regs, 0);
-
- /* select sensor id */
- hisi_thermal_sensor_select(data->regs, sensor->id);
-
- /* enable module */
- hisi_thermal_enable(data->regs, 1);
-
- usleep_range(3000, 5000);
-
- val = hisi_thermal_get_temperature(data->regs);
-
- mutex_unlock(&data->thermal_lock);
-
- return val;
-}
-
-static void hisi_thermal_enable_bind_irq_sensor
- (struct hisi_thermal_data *data)
-{
- struct hisi_thermal_sensor *sensor;
-
- mutex_lock(&data->thermal_lock);
-
- sensor = &data->sensors;
-
- /* setting the hdak time */
- hisi_thermal_hdak_set(data->regs, 0);
-
- /* disable module firstly */
- hisi_thermal_reset_enable(data->regs, 0);
- hisi_thermal_enable(data->regs, 0);
-
- /* select sensor id */
- hisi_thermal_sensor_select(data->regs, sensor->id);
-
- /* enable for interrupt */
- hisi_thermal_alarm_set(data->regs, sensor->thres_temp);
-
- hisi_thermal_reset_set(data->regs, HISI_TEMP_RESET);
-
- /* enable module */
- hisi_thermal_reset_enable(data->regs, 1);
- hisi_thermal_enable(data->regs, 1);
-
- hisi_thermal_alarm_clear(data->regs, 0);
- hisi_thermal_alarm_enable(data->regs, 1);
-
- usleep_range(3000, 5000);
-
- mutex_unlock(&data->thermal_lock);
-}
-
static void hisi_thermal_disable_sensor(struct hisi_thermal_data *data)
{
mutex_lock(&data->thermal_lock);
@@ -253,25 +218,10 @@ static int hisi_thermal_get_temp(void *_sensor, int *temp)
struct hisi_thermal_sensor *sensor = _sensor;
struct hisi_thermal_data *data = sensor->thermal;
- *temp = hisi_thermal_get_sensor_temp(data, sensor);
-
- dev_dbg(&data->pdev->dev, "id=%d, irq=%d, temp=%d, thres=%d\n",
- sensor->id, data->irq_enabled, *temp, sensor->thres_temp);
- /*
- * Bind irq to sensor for two cases:
- * Reenable alarm IRQ if temperature below threshold;
- * if irq has been enabled, always set it;
- */
- if (data->irq_enabled) {
- hisi_thermal_enable_bind_irq_sensor(data);
- return 0;
- }
+ *temp = hisi_thermal_get_temperature(data->regs);
- if (*temp < sensor->thres_temp) {
- data->irq_enabled = true;
- hisi_thermal_enable_bind_irq_sensor(data);
- enable_irq(data->irq);
- }
+ dev_dbg(&data->pdev->dev, "id=%d, temp=%d, thres=%d\n",
+ sensor->id, *temp, sensor->thres_temp);
return 0;
}
@@ -280,26 +230,27 @@ static const struct thermal_zone_of_device_ops hisi_of_thermal_ops = {
.get_temp = hisi_thermal_get_temp,
};
-static irqreturn_t hisi_thermal_alarm_irq(int irq, void *dev)
+static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev)
{
struct hisi_thermal_data *data = dev;
+ struct hisi_thermal_sensor *sensor = &data->sensors;
+ int temp;
- disable_irq_nosync(irq);
- data->irq_enabled = false;
+ hisi_thermal_alarm_clear(data->regs, 1);
- return IRQ_WAKE_THREAD;
-}
+ temp = hisi_thermal_get_temperature(data->regs);
-static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev)
-{
- struct hisi_thermal_data *data = dev;
- struct hisi_thermal_sensor *sensor = &data->sensors;
+ if (temp >= sensor->thres_temp) {
+ dev_crit(&data->pdev->dev, "THERMAL ALARM: %d > %d\n",
+ temp, sensor->thres_temp);
- dev_crit(&data->pdev->dev, "THERMAL ALARM: T > %d\n",
- sensor->thres_temp);
+ thermal_zone_device_update(data->sensors.tzd,
+ THERMAL_EVENT_UNSPECIFIED);
- thermal_zone_device_update(data->sensors.tzd,
- THERMAL_EVENT_UNSPECIFIED);
+ } else if (temp < sensor->thres_temp) {
+ dev_crit(&data->pdev->dev, "THERMAL ALARM stopped: %d < %d\n",
+ temp, sensor->thres_temp);
+ }
return IRQ_HANDLED;
}
@@ -352,6 +303,40 @@ static void hisi_thermal_toggle_sensor(struct hisi_thermal_sensor *sensor,
on ? THERMAL_DEVICE_ENABLED : THERMAL_DEVICE_DISABLED);
}
+static int hisi_thermal_setup(struct hisi_thermal_data *data)
+{
+ struct hisi_thermal_sensor *sensor;
+
+ sensor = &data->sensors;
+
+ /* disable module firstly */
+ hisi_thermal_reset_enable(data->regs, 0);
+ hisi_thermal_enable(data->regs, 0);
+
+ /* select sensor id */
+ hisi_thermal_sensor_select(data->regs, sensor->id);
+
+ /* setting the hdak time */
+ hisi_thermal_hdak_set(data->regs, 0);
+
+ /* setting lag value between current temp and the threshold */
+ hisi_thermal_set_lag(data->regs, HISI_TEMP_LAG);
+
+ /* enable for interrupt */
+ hisi_thermal_alarm_set(data->regs, sensor->thres_temp);
+
+ hisi_thermal_reset_set(data->regs, HISI_TEMP_RESET);
+
+ /* enable module */
+ hisi_thermal_reset_enable(data->regs, 1);
+ hisi_thermal_enable(data->regs, 1);
+
+ hisi_thermal_alarm_clear(data->regs, 0);
+ hisi_thermal_alarm_enable(data->regs, 1);
+
+ return 0;
+}
+
static int hisi_thermal_probe(struct platform_device *pdev)
{
struct hisi_thermal_data *data;
@@ -394,9 +379,6 @@ static int hisi_thermal_probe(struct platform_device *pdev)
return ret;
}
- hisi_thermal_enable_bind_irq_sensor(data);
- data->irq_enabled = true;
-
ret = hisi_thermal_register_sensor(pdev, data,
&data->sensors,
HISI_DEFAULT_SENSOR);
@@ -406,18 +388,21 @@ static int hisi_thermal_probe(struct platform_device *pdev)
return ret;
}
- hisi_thermal_toggle_sensor(&data->sensors, true);
+ ret = hisi_thermal_setup(data);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to setup the sensor: %d\n", ret);
+ return ret;
+ }
- ret = devm_request_threaded_irq(&pdev->dev, data->irq,
- hisi_thermal_alarm_irq,
+ ret = devm_request_threaded_irq(&pdev->dev, data->irq, NULL,
hisi_thermal_alarm_irq_thread,
- 0, "hisi_thermal", data);
+ IRQF_ONESHOT, "hisi_thermal", data);
if (ret < 0) {
dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret);
return ret;
}
- enable_irq(data->irq);
+ hisi_thermal_toggle_sensor(&data->sensors, true);
return 0;
}
@@ -440,7 +425,6 @@ static int hisi_thermal_suspend(struct device *dev)
struct hisi_thermal_data *data = dev_get_drvdata(dev);
hisi_thermal_disable_sensor(data);
- data->irq_enabled = false;
clk_disable_unprepare(data->clk);
@@ -456,8 +440,7 @@ static int hisi_thermal_resume(struct device *dev)
if (ret)
return ret;
- data->irq_enabled = true;
- hisi_thermal_enable_bind_irq_sensor(data);
+ hisi_thermal_setup(data);
return 0;
}
--
2.7.4
The mutex is used to protect against writes in the configuration register.
That happens at probe time, with no possible race yet.
Then when the module is unloaded and at suspend/resume.
When the module is unloaded, it is an userspace operation, thus via a process.
Suspending the system goes through the freezer to suspend all the tasks
synchronously before continuing. So it is not possible to hit the suspend ops
in this driver while we are unloading it.
The resume is the same situation than the probe.
In other words, even if there are several places where we write the
configuration register, there is no situation where we can write it at the same
time, so far as I can judge
Signed-off-by: Daniel Lezcano <[email protected]>
Reviewed-by: Leo Yan <[email protected]>
Tested-by: Leo Yan <[email protected]>
---
drivers/thermal/hisi_thermal.c | 6 ------
1 file changed, 6 deletions(-)
diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c
index cce3d10..39f4627 100644
--- a/drivers/thermal/hisi_thermal.c
+++ b/drivers/thermal/hisi_thermal.c
@@ -53,7 +53,6 @@ struct hisi_thermal_sensor {
};
struct hisi_thermal_data {
- struct mutex thermal_lock; /* protects register data */
struct platform_device *pdev;
struct clk *clk;
struct hisi_thermal_sensor sensor;
@@ -200,14 +199,10 @@ static inline void hisi_thermal_hdak_set(void __iomem *addr, int value)
static void hisi_thermal_disable_sensor(struct hisi_thermal_data *data)
{
- mutex_lock(&data->thermal_lock);
-
/* disable sensor module */
hisi_thermal_enable(data->regs, 0);
hisi_thermal_alarm_enable(data->regs, 0);
hisi_thermal_reset_enable(data->regs, 0);
-
- mutex_unlock(&data->thermal_lock);
}
static int hisi_thermal_get_temp(void *__data, int *temp)
@@ -344,7 +339,6 @@ static int hisi_thermal_probe(struct platform_device *pdev)
if (!data)
return -ENOMEM;
- mutex_init(&data->thermal_lock);
data->pdev = pdev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
--
2.7.4
There is no point to specify the temperature as long variable, the int is
enough.
Replace all long variables to int, so making the code consistent.
Signed-off-by: Daniel Lezcano <[email protected]>
Reviewed-by: Leo Yan <[email protected]>
---
drivers/thermal/hisi_thermal.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c
index 4c47b4d..0c06d14 100644
--- a/drivers/thermal/hisi_thermal.c
+++ b/drivers/thermal/hisi_thermal.c
@@ -85,12 +85,12 @@ static inline int hisi_thermal_step_to_temp(int step)
return HISI_TEMP_BASE + (step * HISI_TEMP_STEP);
}
-static inline long hisi_thermal_temp_to_step(long temp)
+static inline int hisi_thermal_temp_to_step(int temp)
{
return (temp - HISI_TEMP_BASE) / HISI_TEMP_STEP;
}
-static inline long hisi_thermal_round_temp(int temp)
+static inline int hisi_thermal_round_temp(int temp)
{
return hisi_thermal_step_to_temp(
hisi_thermal_temp_to_step(temp));
--
2.7.4
The presence of the thermal data pointer in the sensor structure has the unique
purpose of accessing the thermal data in the interrupt handler.
The sensor pointer is passed when registering the interrupt handler, replace the
cookie by the thermal data pointer, so the back pointer is no longer needed.
Signed-off-by: Daniel Lezcano <[email protected]>
---
drivers/thermal/hisi_thermal.c | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c
index 0c06d14..cce3d10 100644
--- a/drivers/thermal/hisi_thermal.c
+++ b/drivers/thermal/hisi_thermal.c
@@ -47,7 +47,6 @@
#define HISI_DEFAULT_SENSOR 2
struct hisi_thermal_sensor {
- struct hisi_thermal_data *thermal;
struct thermal_zone_device *tzd;
uint32_t id;
uint32_t thres_temp;
@@ -211,10 +210,10 @@ static void hisi_thermal_disable_sensor(struct hisi_thermal_data *data)
mutex_unlock(&data->thermal_lock);
}
-static int hisi_thermal_get_temp(void *_sensor, int *temp)
+static int hisi_thermal_get_temp(void *__data, int *temp)
{
- struct hisi_thermal_sensor *sensor = _sensor;
- struct hisi_thermal_data *data = sensor->thermal;
+ struct hisi_thermal_data *data = __data;
+ struct hisi_thermal_sensor *sensor = &data->sensor;
*temp = hisi_thermal_get_temperature(data->regs);
@@ -262,10 +261,10 @@ static int hisi_thermal_register_sensor(struct platform_device *pdev,
const struct thermal_trip *trip;
sensor->id = index;
- sensor->thermal = data;
sensor->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev,
- sensor->id, sensor, &hisi_of_thermal_ops);
+ sensor->id, data,
+ &hisi_of_thermal_ops);
if (IS_ERR(sensor->tzd)) {
ret = PTR_ERR(sensor->tzd);
sensor->tzd = NULL;
--
2.7.4
The step and the base temperature are fixed values, we can simplify the
computation by converting the base temperature to milli celsius and use a
pre-computed step value. That saves us a lot of mult + div for nothing at
runtime.
Take also the opportunity to change the function names to be consistent with
the rest of the code.
Signed-off-by: Daniel Lezcano <[email protected]>
Reviewed-by: Leo Yan <[email protected]>
Tested-by: Leo Yan <[email protected]>
---
drivers/thermal/hisi_thermal.c | 41 ++++++++++++++++++++++++++++-------------
1 file changed, 28 insertions(+), 13 deletions(-)
diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c
index f69aea0..583bc19 100644
--- a/drivers/thermal/hisi_thermal.c
+++ b/drivers/thermal/hisi_thermal.c
@@ -35,8 +35,9 @@
#define TEMP0_RST_MSK (0x1C)
#define TEMP0_VALUE (0x28)
-#define HISI_TEMP_BASE (-60)
+#define HISI_TEMP_BASE (-60000)
#define HISI_TEMP_RESET (100000)
+#define HISI_TEMP_STEP (784)
#define HISI_MAX_SENSORS 4
#define HISI_DEFAULT_SENSOR 2
@@ -61,19 +62,32 @@ struct hisi_thermal_data {
void __iomem *regs;
};
-/* in millicelsius */
-static inline int _step_to_temp(int step)
+/*
+ * The temperature computation on the tsensor is as follow:
+ * Unit: millidegree Celsius
+ * Step: 255/200 (0.7843)
+ * Temperature base: -60°C
+ *
+ * The register is programmed in temperature steps, every step is 784
+ * millidegree and begins at -60 000 m°C
+ *
+ * The temperature from the steps:
+ *
+ * Temp = TempBase + (steps x 784)
+ *
+ * and the steps from the temperature:
+ *
+ * steps = (Temp - TempBase) / 784
+ *
+ */
+static inline int hisi_thermal_step_to_temp(int step)
{
- /*
- * Every step equals (1 * 200) / 255 celsius, and finally
- * need convert to millicelsius.
- */
- return (HISI_TEMP_BASE * 1000 + (step * 200000 / 255));
+ return HISI_TEMP_BASE + (step * HISI_TEMP_STEP);
}
-static inline long _temp_to_step(long temp)
+static inline long hisi_thermal_temp_to_step(long temp)
{
- return ((temp - HISI_TEMP_BASE * 1000) * 255) / 200000;
+ return (temp - HISI_TEMP_BASE) / HISI_TEMP_STEP;
}
static long hisi_thermal_get_sensor_temp(struct hisi_thermal_data *data,
@@ -99,7 +113,7 @@ static long hisi_thermal_get_sensor_temp(struct hisi_thermal_data *data,
usleep_range(3000, 5000);
val = readl(data->regs + TEMP0_VALUE);
- val = _step_to_temp(val);
+ val = hisi_thermal_step_to_temp(val);
mutex_unlock(&data->thermal_lock);
@@ -126,10 +140,11 @@ static void hisi_thermal_enable_bind_irq_sensor
writel((sensor->id << 12), data->regs + TEMP0_CFG);
/* enable for interrupt */
- writel(_temp_to_step(sensor->thres_temp) | 0x0FFFFFF00,
+ writel(hisi_thermal_temp_to_step(sensor->thres_temp) | 0x0FFFFFF00,
data->regs + TEMP0_TH);
- writel(_temp_to_step(HISI_TEMP_RESET), data->regs + TEMP0_RST_TH);
+ writel(hisi_thermal_temp_to_step(HISI_TEMP_RESET),
+ data->regs + TEMP0_RST_TH);
/* enable module */
writel(0x1, data->regs + TEMP0_RST_MSK);
--
2.7.4
Rename the 'sensors' field to 'sensor' as we describe only one sensor.
Remove the 'sensor_temp' as it is no longer used.
Signed-off-by: Daniel Lezcano <[email protected]>
Reviewed-by: Leo Yan <[email protected]>
Tested-by: Leo Yan <[email protected]>
---
drivers/thermal/hisi_thermal.c | 18 ++++++++----------
1 file changed, 8 insertions(+), 10 deletions(-)
diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c
index 3ce9a4f..4c47b4d 100644
--- a/drivers/thermal/hisi_thermal.c
+++ b/drivers/thermal/hisi_thermal.c
@@ -49,8 +49,6 @@
struct hisi_thermal_sensor {
struct hisi_thermal_data *thermal;
struct thermal_zone_device *tzd;
-
- long sensor_temp;
uint32_t id;
uint32_t thres_temp;
};
@@ -59,9 +57,9 @@ struct hisi_thermal_data {
struct mutex thermal_lock; /* protects register data */
struct platform_device *pdev;
struct clk *clk;
- struct hisi_thermal_sensor sensors;
- int irq;
+ struct hisi_thermal_sensor sensor;
void __iomem *regs;
+ int irq;
};
/*
@@ -233,7 +231,7 @@ static const struct thermal_zone_of_device_ops hisi_of_thermal_ops = {
static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev)
{
struct hisi_thermal_data *data = dev;
- struct hisi_thermal_sensor *sensor = &data->sensors;
+ struct hisi_thermal_sensor *sensor = &data->sensor;
int temp;
hisi_thermal_alarm_clear(data->regs, 1);
@@ -244,7 +242,7 @@ static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev)
dev_crit(&data->pdev->dev, "THERMAL ALARM: %d > %d\n",
temp, sensor->thres_temp);
- thermal_zone_device_update(data->sensors.tzd,
+ thermal_zone_device_update(data->sensor.tzd,
THERMAL_EVENT_UNSPECIFIED);
} else if (temp < sensor->thres_temp) {
@@ -307,7 +305,7 @@ static int hisi_thermal_setup(struct hisi_thermal_data *data)
{
struct hisi_thermal_sensor *sensor;
- sensor = &data->sensors;
+ sensor = &data->sensor;
/* disable module firstly */
hisi_thermal_reset_enable(data->regs, 0);
@@ -380,7 +378,7 @@ static int hisi_thermal_probe(struct platform_device *pdev)
}
ret = hisi_thermal_register_sensor(pdev, data,
- &data->sensors,
+ &data->sensor,
HISI_DEFAULT_SENSOR);
if (ret) {
dev_err(&pdev->dev, "failed to register thermal sensor: %d\n",
@@ -402,7 +400,7 @@ static int hisi_thermal_probe(struct platform_device *pdev)
return ret;
}
- hisi_thermal_toggle_sensor(&data->sensors, true);
+ hisi_thermal_toggle_sensor(&data->sensor, true);
return 0;
}
@@ -410,7 +408,7 @@ static int hisi_thermal_probe(struct platform_device *pdev)
static int hisi_thermal_remove(struct platform_device *pdev)
{
struct hisi_thermal_data *data = platform_get_drvdata(pdev);
- struct hisi_thermal_sensor *sensor = &data->sensors;
+ struct hisi_thermal_sensor *sensor = &data->sensor;
hisi_thermal_toggle_sensor(sensor, false);
hisi_thermal_disable_sensor(data);
--
2.7.4
The TEMP0_CFG configuration register contains different field to set up the
temperature controller. However in the code, nothing prevents a setup to
overwrite the previous one: eg. writing the hdak value overwrites the sensor
selection, the sensor selection overwrites the hdak value.
In order to prevent such thing, use a regmap-like mechanism by reading the
value before, set the corresponding bits and write the result.
Signed-off-by: Daniel Lezcano <[email protected]>
---
drivers/thermal/hisi_thermal.c | 34 +++++++++++++++++++++++++++++-----
1 file changed, 29 insertions(+), 5 deletions(-)
diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c
index 7747b96..10ef6bc 100644
--- a/drivers/thermal/hisi_thermal.c
+++ b/drivers/thermal/hisi_thermal.c
@@ -30,6 +30,8 @@
#define TEMP0_TH (0x4)
#define TEMP0_RST_TH (0x8)
#define TEMP0_CFG (0xC)
+#define TEMP0_CFG_SS_MSK (0xF000)
+#define TEMP0_CFG_HDAK_MSK (0x30)
#define TEMP0_EN (0x10)
#define TEMP0_INT_EN (0x14)
#define TEMP0_INT_CLR (0x18)
@@ -132,19 +134,41 @@ static inline void hisi_thermal_enable(void __iomem *addr, int value)
writel(value, addr + TEMP0_EN);
}
-static inline void hisi_thermal_sensor_select(void __iomem *addr, int sensor)
+static inline int hisi_thermal_get_temperature(void __iomem *addr)
{
- writel((sensor << 12), addr + TEMP0_CFG);
+ return hisi_thermal_step_to_temp(readl(addr + TEMP0_VALUE));
}
-static inline int hisi_thermal_get_temperature(void __iomem *addr)
+/*
+ * Temperature configuration register - Sensor selection
+ *
+ * Bits [19:12]
+ *
+ * 0x0: local sensor (default)
+ * 0x1: remote sensor 1 (ACPU cluster 1)
+ * 0x2: remote sensor 2 (ACPU cluster 0)
+ * 0x3: remote sensor 3 (G3D)
+ */
+static inline void hisi_thermal_sensor_select(void __iomem *addr, int sensor)
{
- return hisi_thermal_step_to_temp(readl(addr + TEMP0_VALUE));
+ writel((readl(addr + TEMP0_CFG) & ~TEMP0_CFG_SS_MSK ) |
+ (sensor << 12), addr + TEMP0_CFG);
}
+/*
+ * Temperature configuration register - Hdak conversion polling interval
+ *
+ * Bits [5:4]
+ *
+ * 0x0 : 0.768 ms
+ * 0x1 : 6.144 ms
+ * 0x2 : 49.152 ms
+ * 0x3 : 393.216 ms
+ */
static inline void hisi_thermal_hdak_set(void __iomem *addr, int value)
{
- writel(value, addr + TEMP0_CFG);
+ writel((readl(addr + TEMP0_CFG) & ~TEMP0_CFG_HDAK_MSK) |
+ (value << 4), addr + TEMP0_CFG);
}
static long hisi_thermal_get_sensor_temp(struct hisi_thermal_data *data,
--
2.7.4
Hopefully, the function name can help to clarify the semantic of the operations
when writing in the register.
Signed-off-by: Daniel Lezcano <[email protected]>
---
drivers/thermal/hisi_thermal.c | 92 ++++++++++++++++++++++++++++++++----------
1 file changed, 70 insertions(+), 22 deletions(-)
diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c
index 9ec5f29..7747b96 100644
--- a/drivers/thermal/hisi_thermal.c
+++ b/drivers/thermal/hisi_thermal.c
@@ -26,6 +26,7 @@
#include "thermal_core.h"
+#define TEMP0_LAG (0x0)
#define TEMP0_TH (0x4)
#define TEMP0_RST_TH (0x8)
#define TEMP0_CFG (0xC)
@@ -96,6 +97,56 @@ static inline long hisi_thermal_round_temp(int temp)
hisi_thermal_temp_to_step(temp));
}
+static inline void hisi_thermal_set_lag(void __iomem *addr, int value)
+{
+ writel(value, addr + TEMP0_LAG);
+}
+
+static inline void hisi_thermal_alarm_clear(void __iomem *addr, int value)
+{
+ writel(value, addr + TEMP0_INT_CLR);
+}
+
+static inline void hisi_thermal_alarm_enable(void __iomem *addr, int value)
+{
+ writel(value, addr + TEMP0_INT_EN);
+}
+
+static inline void hisi_thermal_alarm_set(void __iomem *addr, int temp)
+{
+ writel(hisi_thermal_temp_to_step(temp) | 0x0FFFFFF00, addr + TEMP0_TH);
+}
+
+static inline void hisi_thermal_reset_set(void __iomem *addr, int temp)
+{
+ writel(hisi_thermal_temp_to_step(temp), addr + TEMP0_RST_TH);
+}
+
+static inline void hisi_thermal_reset_enable(void __iomem *addr, int value)
+{
+ writel(value, addr + TEMP0_RST_MSK);
+}
+
+static inline void hisi_thermal_enable(void __iomem *addr, int value)
+{
+ writel(value, addr + TEMP0_EN);
+}
+
+static inline void hisi_thermal_sensor_select(void __iomem *addr, int sensor)
+{
+ writel((sensor << 12), addr + TEMP0_CFG);
+}
+
+static inline int hisi_thermal_get_temperature(void __iomem *addr)
+{
+ return hisi_thermal_step_to_temp(readl(addr + TEMP0_VALUE));
+}
+
+static inline void hisi_thermal_hdak_set(void __iomem *addr, int value)
+{
+ writel(value, addr + TEMP0_CFG);
+}
+
static long hisi_thermal_get_sensor_temp(struct hisi_thermal_data *data,
struct hisi_thermal_sensor *sensor)
{
@@ -104,22 +155,21 @@ static long hisi_thermal_get_sensor_temp(struct hisi_thermal_data *data,
mutex_lock(&data->thermal_lock);
/* disable interrupt */
- writel(0x0, data->regs + TEMP0_INT_EN);
- writel(0x1, data->regs + TEMP0_INT_CLR);
+ hisi_thermal_alarm_enable(data->regs, 0);
+ hisi_thermal_alarm_clear(data->regs, 1);
/* disable module firstly */
- writel(0x0, data->regs + TEMP0_EN);
+ hisi_thermal_enable(data->regs, 0);
/* select sensor id */
- writel((sensor->id << 12), data->regs + TEMP0_CFG);
+ hisi_thermal_sensor_select(data->regs, sensor->id);
/* enable module */
- writel(0x1, data->regs + TEMP0_EN);
+ hisi_thermal_enable(data->regs, 1);
usleep_range(3000, 5000);
- val = readl(data->regs + TEMP0_VALUE);
- val = hisi_thermal_step_to_temp(val);
+ val = hisi_thermal_get_temperature(data->regs);
mutex_unlock(&data->thermal_lock);
@@ -136,28 +186,26 @@ static void hisi_thermal_enable_bind_irq_sensor
sensor = &data->sensors;
/* setting the hdak time */
- writel(0x0, data->regs + TEMP0_CFG);
+ hisi_thermal_hdak_set(data->regs, 0);
/* disable module firstly */
- writel(0x0, data->regs + TEMP0_RST_MSK);
- writel(0x0, data->regs + TEMP0_EN);
+ hisi_thermal_reset_enable(data->regs, 0);
+ hisi_thermal_enable(data->regs, 0);
/* select sensor id */
- writel((sensor->id << 12), data->regs + TEMP0_CFG);
+ hisi_thermal_sensor_select(data->regs, sensor->id);
/* enable for interrupt */
- writel(hisi_thermal_temp_to_step(sensor->thres_temp) | 0x0FFFFFF00,
- data->regs + TEMP0_TH);
+ hisi_thermal_alarm_set(data->regs, sensor->thres_temp);
- writel(hisi_thermal_temp_to_step(HISI_TEMP_RESET),
- data->regs + TEMP0_RST_TH);
+ hisi_thermal_reset_set(data->regs, HISI_TEMP_RESET);
/* enable module */
- writel(0x1, data->regs + TEMP0_RST_MSK);
- writel(0x1, data->regs + TEMP0_EN);
+ hisi_thermal_reset_enable(data->regs, 1);
+ hisi_thermal_enable(data->regs, 1);
- writel(0x0, data->regs + TEMP0_INT_CLR);
- writel(0x1, data->regs + TEMP0_INT_EN);
+ hisi_thermal_alarm_clear(data->regs, 0);
+ hisi_thermal_alarm_enable(data->regs, 1);
usleep_range(3000, 5000);
@@ -169,9 +217,9 @@ static void hisi_thermal_disable_sensor(struct hisi_thermal_data *data)
mutex_lock(&data->thermal_lock);
/* disable sensor module */
- writel(0x0, data->regs + TEMP0_INT_EN);
- writel(0x0, data->regs + TEMP0_RST_MSK);
- writel(0x0, data->regs + TEMP0_EN);
+ hisi_thermal_enable(data->regs, 0);
+ hisi_thermal_alarm_enable(data->regs, 0);
+ hisi_thermal_reset_enable(data->regs, 0);
mutex_unlock(&data->thermal_lock);
}
--
2.7.4
By essence, the tsensor does not really support multiple sensor at the same
time. It allows to set a sensor and use it to get the temperature, another
sensor could be switched but with a delay of 3-5ms. It is difficult to read
simultaneously several sensors without a big delay.
Today, just one sensor is used, it is not necessary to deal with multiple
sensors in the code. Remove them and if it is needed in the future add them
on top of a code which will be clean up in the meantime.
Signed-off-by: Daniel Lezcano <[email protected]>
---
drivers/thermal/hisi_thermal.c | 75 +++++++++++-------------------------------
1 file changed, 19 insertions(+), 56 deletions(-)
diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c
index 8381696..725d0d4 100644
--- a/drivers/thermal/hisi_thermal.c
+++ b/drivers/thermal/hisi_thermal.c
@@ -39,6 +39,7 @@
#define HISI_TEMP_RESET (100000)
#define HISI_MAX_SENSORS 4
+#define HISI_DEFAULT_SENSOR 2
struct hisi_thermal_sensor {
struct hisi_thermal_data *thermal;
@@ -53,9 +54,8 @@ struct hisi_thermal_data {
struct mutex thermal_lock; /* protects register data */
struct platform_device *pdev;
struct clk *clk;
- struct hisi_thermal_sensor sensors[HISI_MAX_SENSORS];
-
- int irq, irq_bind_sensor;
+ struct hisi_thermal_sensor sensors;
+ int irq;
bool irq_enabled;
void __iomem *regs;
@@ -113,7 +113,7 @@ static void hisi_thermal_enable_bind_irq_sensor
mutex_lock(&data->thermal_lock);
- sensor = &data->sensors[data->irq_bind_sensor];
+ sensor = &data->sensors;
/* setting the hdak time */
writel(0x0, data->regs + TEMP0_CFG);
@@ -160,31 +160,8 @@ static int hisi_thermal_get_temp(void *_sensor, int *temp)
struct hisi_thermal_sensor *sensor = _sensor;
struct hisi_thermal_data *data = sensor->thermal;
- int sensor_id = -1, i;
- long max_temp = 0;
-
*temp = hisi_thermal_get_sensor_temp(data, sensor);
- sensor->sensor_temp = *temp;
-
- for (i = 0; i < HISI_MAX_SENSORS; i++) {
- if (!data->sensors[i].tzd)
- continue;
-
- if (data->sensors[i].sensor_temp >= max_temp) {
- max_temp = data->sensors[i].sensor_temp;
- sensor_id = i;
- }
- }
-
- /* If no sensor has been enabled, then skip to enable irq */
- if (sensor_id == -1)
- return 0;
-
- mutex_lock(&data->thermal_lock);
- data->irq_bind_sensor = sensor_id;
- mutex_unlock(&data->thermal_lock);
-
dev_dbg(&data->pdev->dev, "id=%d, irq=%d, temp=%d, thres=%d\n",
sensor->id, data->irq_enabled, *temp, sensor->thres_temp);
/*
@@ -197,7 +174,7 @@ static int hisi_thermal_get_temp(void *_sensor, int *temp)
return 0;
}
- if (max_temp < sensor->thres_temp) {
+ if (*temp < sensor->thres_temp) {
data->irq_enabled = true;
hisi_thermal_enable_bind_irq_sensor(data);
enable_irq(data->irq);
@@ -224,22 +201,16 @@ static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev)
{
struct hisi_thermal_data *data = dev;
struct hisi_thermal_sensor *sensor;
- int i;
mutex_lock(&data->thermal_lock);
- sensor = &data->sensors[data->irq_bind_sensor];
+ sensor = &data->sensors;
dev_crit(&data->pdev->dev, "THERMAL ALARM: T > %d\n",
sensor->thres_temp / 1000);
mutex_unlock(&data->thermal_lock);
- for (i = 0; i < HISI_MAX_SENSORS; i++) {
- if (!data->sensors[i].tzd)
- continue;
-
- thermal_zone_device_update(data->sensors[i].tzd,
- THERMAL_EVENT_UNSPECIFIED);
- }
+ thermal_zone_device_update(data->sensors.tzd,
+ THERMAL_EVENT_UNSPECIFIED);
return IRQ_HANDLED;
}
@@ -296,7 +267,6 @@ static int hisi_thermal_probe(struct platform_device *pdev)
{
struct hisi_thermal_data *data;
struct resource *res;
- int i;
int ret;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
@@ -347,16 +317,17 @@ static int hisi_thermal_probe(struct platform_device *pdev)
hisi_thermal_enable_bind_irq_sensor(data);
data->irq_enabled = true;
- for (i = 0; i < HISI_MAX_SENSORS; ++i) {
- ret = hisi_thermal_register_sensor(pdev, data,
- &data->sensors[i], i);
- if (ret)
- dev_err(&pdev->dev,
- "failed to register thermal sensor: %d\n", ret);
- else
- hisi_thermal_toggle_sensor(&data->sensors[i], true);
+ ret = hisi_thermal_register_sensor(pdev, data,
+ &data->sensors,
+ HISI_DEFAULT_SENSOR);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register thermal sensor: %d\n",
+ ret);
+ return ret;
}
+ hisi_thermal_toggle_sensor(&data->sensors, true);
+
enable_irq(data->irq);
return 0;
@@ -365,17 +336,9 @@ static int hisi_thermal_probe(struct platform_device *pdev)
static int hisi_thermal_remove(struct platform_device *pdev)
{
struct hisi_thermal_data *data = platform_get_drvdata(pdev);
- int i;
-
- for (i = 0; i < HISI_MAX_SENSORS; i++) {
- struct hisi_thermal_sensor *sensor = &data->sensors[i];
-
- if (!sensor->tzd)
- continue;
-
- hisi_thermal_toggle_sensor(sensor, false);
- }
+ struct hisi_thermal_sensor *sensor = &data->sensors;
+ hisi_thermal_toggle_sensor(sensor, false);
hisi_thermal_disable_sensor(data);
clk_disable_unprepare(data->clk);
--
2.7.4
On Mon, Sep 04, 2017 at 09:56:05PM +0200, Daniel Lezcano wrote:
> The threaded interrupt inspect the sensors structure to look in the temp
> threshold field, but this field is read-only in all the code, except in the
> probe function before the threaded interrupt is set. In other words there
> is not race window in the threaded interrupt when reading the field value.
Sure? Not even if you have a userspace process read the zone temp via
sysfs and the polling workqueue reading the temp too?
>
> Signed-off-by: Daniel Lezcano <[email protected]>
> Reviewed-by: Leo Yan <[email protected]>
> ---
> drivers/thermal/hisi_thermal.c | 6 +-----
> 1 file changed, 1 insertion(+), 5 deletions(-)
>
> diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c
> index f523197..9ec5f29 100644
> --- a/drivers/thermal/hisi_thermal.c
> +++ b/drivers/thermal/hisi_thermal.c
> @@ -221,14 +221,10 @@ static irqreturn_t hisi_thermal_alarm_irq(int irq, void *dev)
> static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev)
> {
> struct hisi_thermal_data *data = dev;
> - struct hisi_thermal_sensor *sensor;
> -
> - mutex_lock(&data->thermal_lock);
> - sensor = &data->sensors;
> + struct hisi_thermal_sensor *sensor = &data->sensors;
>
> dev_crit(&data->pdev->dev, "THERMAL ALARM: T > %d\n",
> sensor->thres_temp);
> - mutex_unlock(&data->thermal_lock);
>
> thermal_zone_device_update(data->sensors.tzd,
> THERMAL_EVENT_UNSPECIFIED);
> --
> 2.7.4
>
On Mon, Sep 04, 2017 at 09:56:08PM +0200, Daniel Lezcano wrote:
> The sensor is all setup, bind, resetted, acked, etc... every single second.
>
> That was the way to workaround a problem with the interrupt bouncing again and
> again.
>
> With the following changes, we fix all in one:
>
> - Do the setup, one time, at probe time
>
> - Add the IRQF_ONESHOT, ack the interrupt in the threaded handler
>
> - Remove the interrupt handler
>
> - Set the correct value for the LAG register
>
> - Remove all the irq_enabled stuff in the code as the interruption
> handling is fixed
>
> - Remove the 3ms delay
>
> - Reorder the initialization routine to be in the right order
>
> It ends up to a nicer code and more efficient, the 3-5ms delay is removed from
> the get_temp() path.
It would be good if you could include in your commit message why before
the 3-5ms was needed and now you dont need it. These delays are typical
on ADC conversion time in order to get the proper temperature read. One
side effect of removing the delay could be that you would be caching
previously converted value stored in the register interface, which,
depending on your policy and polling setup, could be bad. Say you have a
polling setup for 1s, you would wait 2s to see read temperature, which,
depending on how the zone behaves could be enough to miss a spike.
>
> Signed-off-by: Daniel Lezcano <[email protected]>
> Reviewed-by: Leo Yan <[email protected]>
> Tested-by: Leo Yan <[email protected]>
> ---
> drivers/thermal/hisi_thermal.c | 203 +++++++++++++++++++----------------------
> 1 file changed, 93 insertions(+), 110 deletions(-)
>
> diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c
> index 10ef6bc..3ce9a4f 100644
> --- a/drivers/thermal/hisi_thermal.c
> +++ b/drivers/thermal/hisi_thermal.c
> @@ -41,6 +41,7 @@
> #define HISI_TEMP_BASE (-60000)
> #define HISI_TEMP_RESET (100000)
> #define HISI_TEMP_STEP (784)
> +#define HISI_TEMP_LAG (3500)
>
> #define HISI_MAX_SENSORS 4
> #define HISI_DEFAULT_SENSOR 2
> @@ -60,8 +61,6 @@ struct hisi_thermal_data {
> struct clk *clk;
> struct hisi_thermal_sensor sensors;
> int irq;
> - bool irq_enabled;
> -
> void __iomem *regs;
> };
>
> @@ -99,9 +98,40 @@ static inline long hisi_thermal_round_temp(int temp)
> hisi_thermal_temp_to_step(temp));
> }
>
> +/*
> + * The lag register contains 5 bits encoding the temperature in steps.
> + *
> + * Each time the temperature crosses the threshold boundary, an
> + * interrupt is raised. It could be when the temperature is going
> + * above the threshold or below. However, if the temperature is
> + * fluctuating around this value due to the load, we can receive
> + * several interrupts which may not desired.
> + *
> + * We can setup a temperature representing the delta between the
> + * threshold and the current temperature when the temperature is
> + * decreasing.
> + *
> + * For instance: the lag register is 5?C, the threshold is 65?C, when
> + * the temperature reaches 65?C an interrupt is raised and when the
> + * temperature decrease to 65?C - 5?C another interrupt is raised.
> + *
> + * A very short lag can lead to an interrupt storm, a long lag
> + * increase the latency to react to the temperature changes. In our
> + * case, that is not really a problem as we are polling the
> + * temperature.
> + *
> + * [0:4] : lag register
> + *
> + * The temperature is coded in steps, cf. HISI_TEMP_STEP.
> + *
> + * Min : 0x00 : 0.0 ?C
> + * Max : 0x1F : 24.3 ?C
> + *
> + * The 'value' parameter is in milliCelsius.
> + */
> static inline void hisi_thermal_set_lag(void __iomem *addr, int value)
> {
> - writel(value, addr + TEMP0_LAG);
> + writel((value / HISI_TEMP_STEP) & 0x1F, addr + TEMP0_LAG);
> }
>
> static inline void hisi_thermal_alarm_clear(void __iomem *addr, int value)
> @@ -171,71 +201,6 @@ static inline void hisi_thermal_hdak_set(void __iomem *addr, int value)
> (value << 4), addr + TEMP0_CFG);
> }
>
> -static long hisi_thermal_get_sensor_temp(struct hisi_thermal_data *data,
> - struct hisi_thermal_sensor *sensor)
> -{
> - long val;
> -
> - mutex_lock(&data->thermal_lock);
> -
> - /* disable interrupt */
> - hisi_thermal_alarm_enable(data->regs, 0);
> - hisi_thermal_alarm_clear(data->regs, 1);
> -
> - /* disable module firstly */
> - hisi_thermal_enable(data->regs, 0);
> -
> - /* select sensor id */
> - hisi_thermal_sensor_select(data->regs, sensor->id);
> -
> - /* enable module */
> - hisi_thermal_enable(data->regs, 1);
> -
> - usleep_range(3000, 5000);
> -
> - val = hisi_thermal_get_temperature(data->regs);
> -
> - mutex_unlock(&data->thermal_lock);
> -
> - return val;
> -}
> -
> -static void hisi_thermal_enable_bind_irq_sensor
> - (struct hisi_thermal_data *data)
> -{
> - struct hisi_thermal_sensor *sensor;
> -
> - mutex_lock(&data->thermal_lock);
> -
> - sensor = &data->sensors;
> -
> - /* setting the hdak time */
> - hisi_thermal_hdak_set(data->regs, 0);
> -
> - /* disable module firstly */
> - hisi_thermal_reset_enable(data->regs, 0);
> - hisi_thermal_enable(data->regs, 0);
> -
> - /* select sensor id */
> - hisi_thermal_sensor_select(data->regs, sensor->id);
> -
> - /* enable for interrupt */
> - hisi_thermal_alarm_set(data->regs, sensor->thres_temp);
> -
> - hisi_thermal_reset_set(data->regs, HISI_TEMP_RESET);
> -
> - /* enable module */
> - hisi_thermal_reset_enable(data->regs, 1);
> - hisi_thermal_enable(data->regs, 1);
> -
> - hisi_thermal_alarm_clear(data->regs, 0);
> - hisi_thermal_alarm_enable(data->regs, 1);
> -
> - usleep_range(3000, 5000);
> -
> - mutex_unlock(&data->thermal_lock);
> -}
> -
> static void hisi_thermal_disable_sensor(struct hisi_thermal_data *data)
> {
> mutex_lock(&data->thermal_lock);
> @@ -253,25 +218,10 @@ static int hisi_thermal_get_temp(void *_sensor, int *temp)
> struct hisi_thermal_sensor *sensor = _sensor;
> struct hisi_thermal_data *data = sensor->thermal;
>
> - *temp = hisi_thermal_get_sensor_temp(data, sensor);
> -
> - dev_dbg(&data->pdev->dev, "id=%d, irq=%d, temp=%d, thres=%d\n",
> - sensor->id, data->irq_enabled, *temp, sensor->thres_temp);
> - /*
> - * Bind irq to sensor for two cases:
> - * Reenable alarm IRQ if temperature below threshold;
> - * if irq has been enabled, always set it;
> - */
> - if (data->irq_enabled) {
> - hisi_thermal_enable_bind_irq_sensor(data);
> - return 0;
> - }
> + *temp = hisi_thermal_get_temperature(data->regs);
>
> - if (*temp < sensor->thres_temp) {
> - data->irq_enabled = true;
> - hisi_thermal_enable_bind_irq_sensor(data);
> - enable_irq(data->irq);
> - }
> + dev_dbg(&data->pdev->dev, "id=%d, temp=%d, thres=%d\n",
> + sensor->id, *temp, sensor->thres_temp);
>
> return 0;
> }
> @@ -280,26 +230,27 @@ static const struct thermal_zone_of_device_ops hisi_of_thermal_ops = {
> .get_temp = hisi_thermal_get_temp,
> };
>
> -static irqreturn_t hisi_thermal_alarm_irq(int irq, void *dev)
> +static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev)
> {
> struct hisi_thermal_data *data = dev;
> + struct hisi_thermal_sensor *sensor = &data->sensors;
> + int temp;
>
> - disable_irq_nosync(irq);
> - data->irq_enabled = false;
> + hisi_thermal_alarm_clear(data->regs, 1);
>
> - return IRQ_WAKE_THREAD;
> -}
> + temp = hisi_thermal_get_temperature(data->regs);
>
> -static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev)
> -{
> - struct hisi_thermal_data *data = dev;
> - struct hisi_thermal_sensor *sensor = &data->sensors;
> + if (temp >= sensor->thres_temp) {
> + dev_crit(&data->pdev->dev, "THERMAL ALARM: %d > %d\n",
> + temp, sensor->thres_temp);
>
> - dev_crit(&data->pdev->dev, "THERMAL ALARM: T > %d\n",
> - sensor->thres_temp);
> + thermal_zone_device_update(data->sensors.tzd,
> + THERMAL_EVENT_UNSPECIFIED);
>
> - thermal_zone_device_update(data->sensors.tzd,
> - THERMAL_EVENT_UNSPECIFIED);
> + } else if (temp < sensor->thres_temp) {
> + dev_crit(&data->pdev->dev, "THERMAL ALARM stopped: %d < %d\n",
> + temp, sensor->thres_temp);
> + }
>
> return IRQ_HANDLED;
> }
> @@ -352,6 +303,40 @@ static void hisi_thermal_toggle_sensor(struct hisi_thermal_sensor *sensor,
> on ? THERMAL_DEVICE_ENABLED : THERMAL_DEVICE_DISABLED);
> }
>
> +static int hisi_thermal_setup(struct hisi_thermal_data *data)
> +{
> + struct hisi_thermal_sensor *sensor;
> +
> + sensor = &data->sensors;
> +
> + /* disable module firstly */
> + hisi_thermal_reset_enable(data->regs, 0);
> + hisi_thermal_enable(data->regs, 0);
> +
> + /* select sensor id */
> + hisi_thermal_sensor_select(data->regs, sensor->id);
> +
> + /* setting the hdak time */
> + hisi_thermal_hdak_set(data->regs, 0);
> +
> + /* setting lag value between current temp and the threshold */
> + hisi_thermal_set_lag(data->regs, HISI_TEMP_LAG);
> +
> + /* enable for interrupt */
> + hisi_thermal_alarm_set(data->regs, sensor->thres_temp);
> +
> + hisi_thermal_reset_set(data->regs, HISI_TEMP_RESET);
> +
> + /* enable module */
> + hisi_thermal_reset_enable(data->regs, 1);
> + hisi_thermal_enable(data->regs, 1);
> +
> + hisi_thermal_alarm_clear(data->regs, 0);
> + hisi_thermal_alarm_enable(data->regs, 1);
> +
> + return 0;
> +}
> +
> static int hisi_thermal_probe(struct platform_device *pdev)
> {
> struct hisi_thermal_data *data;
> @@ -394,9 +379,6 @@ static int hisi_thermal_probe(struct platform_device *pdev)
> return ret;
> }
>
> - hisi_thermal_enable_bind_irq_sensor(data);
> - data->irq_enabled = true;
> -
> ret = hisi_thermal_register_sensor(pdev, data,
> &data->sensors,
> HISI_DEFAULT_SENSOR);
> @@ -406,18 +388,21 @@ static int hisi_thermal_probe(struct platform_device *pdev)
> return ret;
> }
>
> - hisi_thermal_toggle_sensor(&data->sensors, true);
> + ret = hisi_thermal_setup(data);
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to setup the sensor: %d\n", ret);
> + return ret;
> + }
>
> - ret = devm_request_threaded_irq(&pdev->dev, data->irq,
> - hisi_thermal_alarm_irq,
> + ret = devm_request_threaded_irq(&pdev->dev, data->irq, NULL,
> hisi_thermal_alarm_irq_thread,
> - 0, "hisi_thermal", data);
> + IRQF_ONESHOT, "hisi_thermal", data);
> if (ret < 0) {
> dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret);
> return ret;
> }
>
> - enable_irq(data->irq);
> + hisi_thermal_toggle_sensor(&data->sensors, true);
>
> return 0;
> }
> @@ -440,7 +425,6 @@ static int hisi_thermal_suspend(struct device *dev)
> struct hisi_thermal_data *data = dev_get_drvdata(dev);
>
> hisi_thermal_disable_sensor(data);
> - data->irq_enabled = false;
>
> clk_disable_unprepare(data->clk);
>
> @@ -456,8 +440,7 @@ static int hisi_thermal_resume(struct device *dev)
> if (ret)
> return ret;
>
> - data->irq_enabled = true;
> - hisi_thermal_enable_bind_irq_sensor(data);
> + hisi_thermal_setup(data);
>
> return 0;
> }
> --
> 2.7.4
>
On Mon, Sep 04, 2017 at 09:56:12PM +0200, Daniel Lezcano wrote:
> The mutex is used to protect against writes in the configuration register.
>
> That happens at probe time, with no possible race yet.
>
> Then when the module is unloaded and at suspend/resume.
>
> When the module is unloaded, it is an userspace operation, thus via a process.
> Suspending the system goes through the freezer to suspend all the tasks
> synchronously before continuing. So it is not possible to hit the suspend ops
> in this driver while we are unloading it.
>
> The resume is the same situation than the probe.
>
> In other words, even if there are several places where we write the
> configuration register, there is no situation where we can write it at the same
> time, so far as I can judge
To me is good to trend towards removal of a lock. Also keep in mind that
the thermal zone has a lock of its own. However, remember that get temp
may be called also from sysfs interaction, from your threaded irq and
from the workqueue in the thermal core that does the polling state
machine.
>
> Signed-off-by: Daniel Lezcano <[email protected]>
> Reviewed-by: Leo Yan <[email protected]>
> Tested-by: Leo Yan <[email protected]>
> ---
> drivers/thermal/hisi_thermal.c | 6 ------
> 1 file changed, 6 deletions(-)
>
> diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c
> index cce3d10..39f4627 100644
> --- a/drivers/thermal/hisi_thermal.c
> +++ b/drivers/thermal/hisi_thermal.c
> @@ -53,7 +53,6 @@ struct hisi_thermal_sensor {
> };
>
> struct hisi_thermal_data {
> - struct mutex thermal_lock; /* protects register data */
> struct platform_device *pdev;
> struct clk *clk;
> struct hisi_thermal_sensor sensor;
> @@ -200,14 +199,10 @@ static inline void hisi_thermal_hdak_set(void __iomem *addr, int value)
>
> static void hisi_thermal_disable_sensor(struct hisi_thermal_data *data)
> {
> - mutex_lock(&data->thermal_lock);
> -
> /* disable sensor module */
> hisi_thermal_enable(data->regs, 0);
> hisi_thermal_alarm_enable(data->regs, 0);
> hisi_thermal_reset_enable(data->regs, 0);
> -
> - mutex_unlock(&data->thermal_lock);
> }
>
> static int hisi_thermal_get_temp(void *__data, int *temp)
> @@ -344,7 +339,6 @@ static int hisi_thermal_probe(struct platform_device *pdev)
> if (!data)
> return -ENOMEM;
>
> - mutex_init(&data->thermal_lock);
> data->pdev = pdev;
>
> res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> --
> 2.7.4
>
On 08/09/2017 05:16, Eduardo Valentin wrote:
> On Mon, Sep 04, 2017 at 09:56:05PM +0200, Daniel Lezcano wrote:
>> The threaded interrupt inspect the sensors structure to look in the temp
>> threshold field, but this field is read-only in all the code, except in the
>> probe function before the threaded interrupt is set. In other words there
>> is not race window in the threaded interrupt when reading the field value.
>
>
> Sure? Not even if you have a userspace process read the zone temp via
> sysfs and the polling workqueue reading the temp too?
They are both read-only operations. There is no write-read race.
--
<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
On 08/09/2017 05:22, Eduardo Valentin wrote:
> On Mon, Sep 04, 2017 at 09:56:08PM +0200, Daniel Lezcano wrote:
>> The sensor is all setup, bind, resetted, acked, etc... every single second.
>>
>> That was the way to workaround a problem with the interrupt bouncing again and
>> again.
>>
>> With the following changes, we fix all in one:
>>
>> - Do the setup, one time, at probe time
>>
>> - Add the IRQF_ONESHOT, ack the interrupt in the threaded handler
>>
>> - Remove the interrupt handler
>>
>> - Set the correct value for the LAG register
>>
>> - Remove all the irq_enabled stuff in the code as the interruption
>> handling is fixed
>>
>> - Remove the 3ms delay
>>
>> - Reorder the initialization routine to be in the right order
>>
>> It ends up to a nicer code and more efficient, the 3-5ms delay is removed from
>> the get_temp() path.
>
> It would be good if you could include in your commit message why before
> the 3-5ms was needed and now you dont need it. These delays are typical
> on ADC conversion time in order to get the proper temperature read. One
> side effect of removing the delay could be that you would be caching
> previously converted value stored in the register interface, which,
> depending on your policy and polling setup, could be bad. Say you have a
> polling setup for 1s, you would wait 2s to see read temperature, which,
> depending on how the zone behaves could be enough to miss a spike.
May be I was unclear. The 3-5ms delay is to *setup* the sensor. There
was a misunderstanding of how the sensor was working when the driver was
initially implemented and the interrupt behavior was erratic. In order
to workaround this, the sensor was reseted and setup every single get
temp, consequently the delay is to let the sensor initializes itself
before reading the temperature, an insane hack actually. I didn't feel
comfortable to put that in the changelog.
All the series fixes this and it results in having the setup done at
boot time. The sensor has way enough time to initialize itself before
the first reading.
Reading the temperature is just to read the register which is not a
relaxed one, thus with a rmb().
Do you want me to update the changelog or does the explanation make the
changelog more clear?
--
<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