Presented patches add support for Odroid's U3 optional CPU FAN, which uses PWM
subsystem for low level control.
After successful probe it registers itself as a cooling device for thermal
subsystem. To preserve ability to use this fan as a PWM device a stub for
thermal_of_cooling_device_register() has been added.
Moreover, some entries to device tree description for Exynos4412 and in
particular Odroid U3 have been added.
Those patches were tested on Exynos4412 - Odroid U3 board.
Patches were applied on:
linux-soc-thermal/next branch
SHA1: c42c7a44c7a543dcb388c1ee1a798e6ed76ad8cf
with following preceding patch series:
'thermal: exynos: Thermal code rework to use device tree'
http://www.spinics.net/lists/linux-samsung-soc/msg37719.html
Presented code utilize reworked Exynos thermal subsystem.
Kamil Debski (1):
ARM: dts: Add pwm-fan node to the Odroid-U3 board
Lukasz Majewski (8):
thermal: Provide stub for thermal_of_cooling_device_register()
function
hwmon: dts: Doc: Add DTS doc to explain how to use PWM FAN as a
cooling device
hwmon: thermal: dts: Add properties to use pwm-fan device as a cooling
device in Odroid U3
hwmon: thermal: Extract __set_pwm() function to only modify PWM duty
cycle
hwmon: thermal: Read PWM FAN configuration from device tree
hwmon: thermal: Code for using PWM FAN as a cooling device
hwmon: thermal: Provide 'default-pulse-width' property to setup FAN on
boot
hwmon: thermal: dts: Disable FAN on boot on the Odroid U3
.../devicetree/bindings/hwmon/pwm-fan.txt | 28 +++
arch/arm/boot/dts/exynos4.dtsi | 2 +-
arch/arm/boot/dts/exynos4412-odroidu3.dts | 45 +++++
drivers/hwmon/pwm-fan.c | 195 +++++++++++++++++----
include/linux/thermal.h | 14 +-
5 files changed, 249 insertions(+), 35 deletions(-)
--
2.0.0.rc2
Odroid U3 fan can work without being registered as OF cooling device
(with CONFIG_THERMAL_OF disabled).
In this situation it can be controlled via PWM entry at
/sys/class/hwmon/hwmon0/pwm1.
Therefore, the thermal_of_cooling_device_register() function needs a stub
to allow clean compilation.
Signed-off-by: Lukasz Majewski <[email protected]>
---
include/linux/thermal.h | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 99be7fc..54dce0e 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -328,6 +328,10 @@ thermal_zone_of_sensor_register(struct device *dev, int id, void *data,
const struct thermal_zone_of_device_ops *ops);
void thermal_zone_of_sensor_unregister(struct device *dev,
struct thermal_zone_device *tz);
+struct thermal_cooling_device *
+thermal_of_cooling_device_register(struct device_node *np,
+ char *type, void *devdata,
+ const struct thermal_cooling_device_ops *);
#else
static inline struct thermal_zone_device *
thermal_zone_of_sensor_register(struct device *dev, int id, void *data,
@@ -342,6 +346,13 @@ void thermal_zone_of_sensor_unregister(struct device *dev,
{
}
+static inline struct thermal_cooling_device *
+thermal_of_cooling_device_register(struct device_node *np,
+ char *type, void *devdata,
+ const struct thermal_cooling_device_ops *ops)
+{
+ return NULL;
+}
#endif
struct thermal_zone_device *thermal_zone_device_register(const char *, int, int,
void *, struct thermal_zone_device_ops *,
@@ -357,9 +368,6 @@ void thermal_zone_device_update(struct thermal_zone_device *);
struct thermal_cooling_device *thermal_cooling_device_register(char *, void *,
const struct thermal_cooling_device_ops *);
-struct thermal_cooling_device *
-thermal_of_cooling_device_register(struct device_node *np, char *, void *,
- const struct thermal_cooling_device_ops *);
void thermal_cooling_device_unregister(struct thermal_cooling_device *);
struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name);
int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp);
--
2.0.0.rc2
Several new properties to allow PWM fan working as a cooling device have been
combined into this single commit.
Signed-off-by: Lukasz Majewski <[email protected]>
---
.../devicetree/bindings/hwmon/pwm-fan.txt | 28 ++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
index 610757c..3877810 100644
--- a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
+++ b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
@@ -3,10 +3,38 @@ Bindings for a fan connected to the PWM lines
Required properties:
- compatible : "pwm-fan"
- pwms : the PWM that is used to control the PWM fan
+- cooling-pwm-values : PWM duty cycle values relative to
+ cooling-max-pwm-value correspondig to
+ proper cooling states
+- default-pulse-width : Property specifying default pulse width for FAN
+ at system boot (zero to disable FAN on boot).
+ Allowed range is 0 to 255
+
+Thorough description of the following bindings:
+ cooling-min-state = <0>;
+ cooling-max-state = <3>;
+ #cooling-cells = <2>;
+ thermal-zone {
+ cpu_thermal: cpu-thermal {
+ cooling-maps {
+ map0 {
+ trip = <&cpu_alert1>;
+ cooling-device = <&fan0 0 1>;
+ };
+ };
+ };
+
+for PWM FAN used as cooling device can be found at:
+./Documentation/devicetree/bindings/thermal/thermal.txt
Example:
pwm-fan {
compatible = "pwm-fan";
status = "okay";
pwms = <&pwm 0 10000 0>;
+ cooling-min-state = <0>;
+ cooling-max-state = <3>;
+ #cooling-cells = <2>;
+ cooling-pwm-values = <0 102 170 255>;
+ default-pulse-width = <0>;
};
--
2.0.0.rc2
From: Kamil Debski <[email protected]>
Add pwm-fan node to the Odroid-U3 board file to enable PWM control of the
cooling fan. In addition, add the "pwm" label to the pwm@139D0000 node
in the exynos4412.dtsi.
Signed-off-by: Kamil Debski <[email protected]>
[Rebased on the newest mainline by [email protected]]
---
Changes since v1:
- added pwm label to the pwm@139D0000 node in exynos4.dtsi
- use the pwm label in the exynos4412-odroidu3.dts
- change order or properties in the pwn-fan node, to be sorted
in alphabetical order
---
arch/arm/boot/dts/exynos4.dtsi | 2 +-
arch/arm/boot/dts/exynos4412-odroidu3.dts | 13 +++++++++++++
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi
index 1735bb3..f227a39 100644
--- a/arch/arm/boot/dts/exynos4.dtsi
+++ b/arch/arm/boot/dts/exynos4.dtsi
@@ -583,7 +583,7 @@
status = "disabled";
};
- pwm@139D0000 {
+ pwm: pwm@139D0000 {
compatible = "samsung,exynos4210-pwm";
reg = <0x139D0000 0x1000>;
interrupts = <0 37 0>, <0 38 0>, <0 39 0>, <0 40 0>, <0 41 0>;
diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts
index c8a64be..60bd1e4 100644
--- a/arch/arm/boot/dts/exynos4412-odroidu3.dts
+++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts
@@ -31,6 +31,19 @@
linux,default-trigger = "heartbeat";
};
};
+
+ pwm-fan {
+ compatible = "pwm-fan";
+ pwms = <&pwm 0 10000 0>;
+ status = "okay";
+ };
+};
+
+&pwm {
+ pinctrl-0 = <&pwm0_out>;
+ pinctrl-names = "default";
+ samsung,pwm-outputs = <0>;
+ status = "okay";
};
&usb3503 {
--
2.0.0.rc2
With those bindings it is possible to use pwm-fan device available at
Odroid U3 as a cooling device.
Signed-off-by: Lukasz Majewski <[email protected]>
---
arch/arm/boot/dts/exynos4412-odroidu3.dts | 33 ++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts
index 60bd1e4..cc50e96 100644
--- a/arch/arm/boot/dts/exynos4412-odroidu3.dts
+++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts
@@ -32,11 +32,42 @@
};
};
- pwm-fan {
+ fan0: pwm-fan {
compatible = "pwm-fan";
pwms = <&pwm 0 10000 0>;
+ cooling-min-state = <0>;
+ cooling-max-state = <3>;
+ #cooling-cells = <2>;
+ cooling-pwm-values = <0 102 170 255>;
status = "okay";
};
+
+ thermal-zones {
+ cpu_thermal: cpu-thermal {
+ cooling-maps {
+ map0 {
+ trip = <&cpu_alert1>;
+ cooling-device = <&cpu0 7 7>;
+ };
+ map1 {
+ trip = <&cpu_alert2>;
+ cooling-device = <&cpu0 13 13>;
+ };
+ map2 {
+ trip = <&cpu_alert0>;
+ cooling-device = <&fan0 0 1>;
+ };
+ map3 {
+ trip = <&cpu_alert1>;
+ cooling-device = <&fan0 1 2>;
+ };
+ map4 {
+ trip = <&cpu_alert2>;
+ cooling-device = <&fan0 2 3>;
+ };
+ };
+ };
+ };
};
&pwm {
--
2.0.0.rc2
It was necessary to decouple code handling writing to sysfs from the one
responsible for setting PWM of the fan.
Due to that, new __set_pwm() method was extracted, which is responsible for
only setting new PWM duty cycle.
Signed-off-by: Lukasz Majewski <[email protected]>
---
drivers/hwmon/pwm-fan.c | 35 ++++++++++++++++++++++-------------
1 file changed, 22 insertions(+), 13 deletions(-)
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 1991d903..870e100 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -33,21 +33,15 @@ struct pwm_fan_ctx {
unsigned char pwm_value;
};
-static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
{
- struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
- unsigned long pwm, duty;
- ssize_t ret;
-
- if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
- return -EINVAL;
-
- mutex_lock(&ctx->lock);
+ unsigned long duty;
+ int ret;
if (ctx->pwm_value == pwm)
- goto exit_set_pwm_no_change;
+ return 0;
+ mutex_lock(&ctx->lock);
if (pwm == 0) {
pwm_disable(ctx->pwm);
goto exit_set_pwm;
@@ -66,13 +60,28 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
exit_set_pwm:
ctx->pwm_value = pwm;
-exit_set_pwm_no_change:
- ret = count;
exit_set_pwm_err:
mutex_unlock(&ctx->lock);
return ret;
}
+static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
+ unsigned long pwm;
+ int ret;
+
+ if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
+ return -EINVAL;
+
+ ret = __set_pwm(ctx, pwm);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
static ssize_t show_pwm(struct device *dev,
struct device_attribute *attr, char *buf)
{
--
2.0.0.rc2
Code for reading PWM FAN configuration data via device tree has been added.
Signed-off-by: Lukasz Majewski <[email protected]>
---
drivers/hwmon/pwm-fan.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 50 insertions(+), 1 deletion(-)
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 870e100..5854bb3 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -30,7 +30,10 @@
struct pwm_fan_ctx {
struct mutex lock;
struct pwm_device *pwm;
- unsigned char pwm_value;
+ unsigned int pwm_value;
+ unsigned int pwm_fan_state;
+ unsigned int pwm_fan_max_state;
+ unsigned int *pwm_fan_cooling_states;
};
static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
@@ -100,6 +103,49 @@ static struct attribute *pwm_fan_attrs[] = {
ATTRIBUTE_GROUPS(pwm_fan);
+int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
+{
+ struct device_node *np = dev->of_node;
+ struct property *pp;
+ int len, num, i;
+
+ pp = of_find_property(np, "cooling-pwm-values", &len);
+ if (!pp) {
+ dev_err(dev, "Property: 'cooling-pwm-values' not found\n");
+ return -EINVAL;
+ }
+
+ if (len == 0) {
+ dev_err(dev, "Length wrong value!\n");
+ return -EINVAL;
+ }
+
+ ctx->pwm_fan_cooling_states = devm_kzalloc(dev, len, GFP_KERNEL);
+ if (!ctx->pwm_fan_cooling_states) {
+ dev_err(dev, "Allocation failed for pwm dan cooling states\n");
+ return -ENOMEM;
+ }
+
+ num = len / sizeof(u32);
+ if (of_property_read_u32_array(np, pp->name,
+ ctx->pwm_fan_cooling_states, num)) {
+ dev_err(dev, "Property: %s cannot be read!\n", pp->name);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < num; i++) {
+ if (ctx->pwm_fan_cooling_states[i] > MAX_PWM) {
+ dev_err(dev, "PWM fan state[%d]:%d > %d\n", i,
+ ctx->pwm_fan_cooling_states[i], MAX_PWM);
+ return -EINVAL;
+ }
+ }
+
+ ctx->pwm_fan_max_state = num - 1;
+
+ return 0;
+}
+
static int pwm_fan_probe(struct platform_device *pdev)
{
struct device *hwmon;
@@ -120,6 +166,9 @@ static int pwm_fan_probe(struct platform_device *pdev)
}
platform_set_drvdata(pdev, ctx);
+ ret = pwm_fan_of_get_cooling_data(&pdev->dev, ctx);
+ if (ret)
+ return ret;
/* Set duty cycle to maximum allowed */
duty_cycle = ctx->pwm->period - 1;
--
2.0.0.rc2
The PWM FAN device can now be used as a thermal cooling device. Necessary
infrastructure has been added in this commit.
Signed-off-by: Lukasz Majewski <[email protected]>
---
drivers/hwmon/pwm-fan.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 78 insertions(+), 1 deletion(-)
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 5854bb3..97b77e9 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -24,6 +24,7 @@
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/sysfs.h>
+#include <linux/thermal.h>
#define MAX_PWM 255
@@ -68,6 +69,17 @@ exit_set_pwm_err:
return ret;
}
+static void pwm_fan_update_state(struct pwm_fan_ctx *ctx, unsigned long pwm)
+{
+ int i;
+
+ for (i = 0; i < ctx->pwm_fan_max_state; ++i)
+ if (pwm < ctx->pwm_fan_cooling_states[i + 1])
+ break;
+
+ ctx->pwm_fan_state = i;
+}
+
static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -82,6 +94,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
if (ret)
return ret;
+ pwm_fan_update_state(ctx, pwm);
return count;
}
@@ -103,6 +116,59 @@ static struct attribute *pwm_fan_attrs[] = {
ATTRIBUTE_GROUPS(pwm_fan);
+/* thermal cooling device callbacks */
+static int pwm_fan_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+
+ *state = ctx->pwm_fan_max_state;
+
+ return 0;
+}
+
+static int pwm_fan_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+
+ if (!ctx)
+ return -EINVAL;
+
+ *state = ctx->pwm_fan_state;
+
+ return 0;
+}
+
+static int
+pwm_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+ int ret;
+
+ if (!ctx || (state > ctx->pwm_fan_max_state))
+ return -EINVAL;
+
+ if (state == ctx->pwm_fan_state)
+ return 0;
+
+ ret = __set_pwm(ctx, ctx->pwm_fan_cooling_states[state]);
+ if (ret) {
+ dev_err(&cdev->device, "Cannot set pwm!\n");
+ return ret;
+ }
+
+ ctx->pwm_fan_state = state;
+
+ return ret;
+}
+
+static const struct thermal_cooling_device_ops pwm_fan_cooling_ops = {
+ .get_max_state = pwm_fan_get_max_state,
+ .get_cur_state = pwm_fan_get_cur_state,
+ .set_cur_state = pwm_fan_set_cur_state,
+};
+
int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
{
struct device_node *np = dev->of_node;
@@ -148,8 +214,9 @@ int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
static int pwm_fan_probe(struct platform_device *pdev)
{
- struct device *hwmon;
+ struct thermal_cooling_device *cdev;
struct pwm_fan_ctx *ctx;
+ struct device *hwmon;
int duty_cycle;
int ret;
@@ -194,6 +261,16 @@ static int pwm_fan_probe(struct platform_device *pdev)
pwm_disable(ctx->pwm);
return PTR_ERR(hwmon);
}
+
+ cdev = thermal_of_cooling_device_register(pdev->dev.of_node, "pwm-fan",
+ ctx, &pwm_fan_cooling_ops);
+ if (IS_ERR(cdev)) {
+ dev_err(&pdev->dev,
+ "Failed to register pwm-fan as thermal cooling device");
+ pwm_disable(ctx->pwm);
+ return PTR_ERR(cdev);
+ }
+
return 0;
}
--
2.0.0.rc2
Up till now the PWM fan was enabled by default in the pwm-fan driver.
Now, by defining 'default-pulse-width' device tree property, it is possible
to configure the fan RPM on boot. By specifying value of 0, one can disable it.
Signed-off-by: Lukasz Majewski <[email protected]>
---
drivers/hwmon/pwm-fan.c | 34 ++++++++++++++++------------------
1 file changed, 16 insertions(+), 18 deletions(-)
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 97b77e9..c1cf48b 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -34,6 +34,7 @@ struct pwm_fan_ctx {
unsigned int pwm_value;
unsigned int pwm_fan_state;
unsigned int pwm_fan_max_state;
+ unsigned int pwm_fan_default_width;
unsigned int *pwm_fan_cooling_states;
};
@@ -172,8 +173,21 @@ static const struct thermal_cooling_device_ops pwm_fan_cooling_ops = {
int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
{
struct device_node *np = dev->of_node;
+ int len, num, i, ret;
struct property *pp;
- int len, num, i;
+
+ ret = of_property_read_u32(np, "default-pulse-width",
+ &ctx->pwm_fan_default_width);
+ if (ret) {
+ dev_err(dev, "Property: 'default-pulse-width' not found\n");
+ return -EINVAL;
+ }
+
+ if (ctx->pwm_fan_default_width > MAX_PWM) {
+ dev_err(dev, "'default-pulse-width %d larger than %d\n",
+ ctx->pwm_fan_default_width, MAX_PWM);
+ return -EINVAL;
+ }
pp = of_find_property(np, "cooling-pwm-values", &len);
if (!pp) {
@@ -217,7 +231,6 @@ static int pwm_fan_probe(struct platform_device *pdev)
struct thermal_cooling_device *cdev;
struct pwm_fan_ctx *ctx;
struct device *hwmon;
- int duty_cycle;
int ret;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
@@ -237,22 +250,7 @@ static int pwm_fan_probe(struct platform_device *pdev)
if (ret)
return ret;
- /* Set duty cycle to maximum allowed */
- duty_cycle = ctx->pwm->period - 1;
- ctx->pwm_value = MAX_PWM;
-
- ret = pwm_config(ctx->pwm, duty_cycle, ctx->pwm->period);
- if (ret) {
- dev_err(&pdev->dev, "Failed to configure PWM\n");
- return ret;
- }
-
- /* Enbale PWM output */
- ret = pwm_enable(ctx->pwm);
- if (ret) {
- dev_err(&pdev->dev, "Failed to enable PWM\n");
- return ret;
- }
+ __set_pwm(ctx, ctx->pwm_fan_default_width);
hwmon = devm_hwmon_device_register_with_groups(&pdev->dev, "pwmfan",
ctx, pwm_fan_groups);
--
2.0.0.rc2
By specifying default-pulse-width to zero, disable FAN on boot.
Signed-off-by: Lukasz Majewski <[email protected]>
---
arch/arm/boot/dts/exynos4412-odroidu3.dts | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts
index cc50e96..fec063e 100644
--- a/arch/arm/boot/dts/exynos4412-odroidu3.dts
+++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts
@@ -39,6 +39,7 @@
cooling-max-state = <3>;
#cooling-cells = <2>;
cooling-pwm-values = <0 102 170 255>;
+ default-pulse-width = <0>;
status = "okay";
};
--
2.0.0.rc2
On Thu, 2014-12-18 at 11:13 +0100, Lukasz Majewski wrote:
> Several new properties to allow PWM fan working as a cooling device have been
> combined into this single commit.
>
> Signed-off-by: Lukasz Majewski <[email protected]>
> ---
> .../devicetree/bindings/hwmon/pwm-fan.txt | 28 ++++++++++++++++++++++
> 1 file changed, 28 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
> index 610757c..3877810 100644
> --- a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
> +++ b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
> @@ -3,10 +3,38 @@ Bindings for a fan connected to the PWM lines
> Required properties:
> - compatible : "pwm-fan"
> - pwms : the PWM that is used to control the PWM fan
> +- cooling-pwm-values : PWM duty cycle values relative to
> + cooling-max-pwm-value correspondig to
> + proper cooling states
> +- default-pulse-width : Property specifying default pulse width for FAN
> + at system boot (zero to disable FAN on boot).
> + Allowed range is 0 to 255
The 0..255 range seems somewhat random. Would be nicer to either use the
approach of pwm-backlight (iotw, have the range go from the first to the
last entry of cooling-pwm-values) or simply have be the duty lenght in
NS as entries instead of the current indirection.
I assumed your cooling-pwm-values are a [0..255] range as well instead
of nanoseconds (would be good to make that more clear)?
Also having more consistent names would be nice.. To take pwm-backlight
as inspiration, cooling-levels and default-cooling-level would make it
more clear the second property picks a default setting from the first
one.
One thing i do wonder, is having an explicit default setting useful?
Should it not default to maximum cooling unless otherwise configured by
either the thermal framework or sysfs ?
> +Thorough description of the following bindings:
> + cooling-min-state = <0>;
> + cooling-max-state = <3>;
> + #cooling-cells = <2>;
> + thermal-zone {
> + cpu_thermal: cpu-thermal {
> + cooling-maps {
> + map0 {
> + trip = <&cpu_alert1>;
> + cooling-device = <&fan0 0 1>;
> + };
> + };
> + };
> +
> +for PWM FAN used as cooling device can be found at:
> +./Documentation/devicetree/bindings/thermal/thermal.txt
>
> Example:
> pwm-fan {
> compatible = "pwm-fan";
> status = "okay";
> pwms = <&pwm 0 10000 0>;
> + cooling-min-state = <0>;
> + cooling-max-state = <3>;
> + #cooling-cells = <2>;
> + cooling-pwm-values = <0 102 170 255>;
> + default-pulse-width = <0>;
> };
On 12/18/2014 02:13 AM, Lukasz Majewski wrote:
> Several new properties to allow PWM fan working as a cooling device have been
> combined into this single commit.
>
> Signed-off-by: Lukasz Majewski <[email protected]>
> ---
> .../devicetree/bindings/hwmon/pwm-fan.txt | 28 ++++++++++++++++++++++
> 1 file changed, 28 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
> index 610757c..3877810 100644
> --- a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
> +++ b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
> @@ -3,10 +3,38 @@ Bindings for a fan connected to the PWM lines
> Required properties:
> - compatible : "pwm-fan"
> - pwms : the PWM that is used to control the PWM fan
> +- cooling-pwm-values : PWM duty cycle values relative to
> + cooling-max-pwm-value correspondig to
> + proper cooling states
I don't understand what you mean with "relative to". Please elaborate.
Do you mean "associated with" ?
Where is "cooling-max-pwm-value" defined, and how is this all expected
to relate to the maximum duty cycle value provided by the pwm device ?
> +- default-pulse-width : Property specifying default pulse width for FAN
> + at system boot (zero to disable FAN on boot).
> + Allowed range is 0 to 255
You'll need dt maintainer approval for the new properties.
One thing I wonder about though is why you use "default-pulse-width"
and not "default-pwm". Seems to be arbitrary. I don't see "pulse-width"
used anywhere in the upstream kernel.
I am somewhat concerned that you define the new properties as mandatory.
That means existing configurations will fail, which does not seem to be
a good idea. It would be more appropriate to not configure the thermal device
if the new properties are not provided.
The newly introduced semantics also conflicts with the current semantics,
which sets the initial duty cycle initially to the maximum allowed duty cycle
as reported by the pwm device itself.
Guenter
> +
> +Thorough description of the following bindings:
> + cooling-min-state = <0>;
> + cooling-max-state = <3>;
> + #cooling-cells = <2>;
> + thermal-zone {
> + cpu_thermal: cpu-thermal {
> + cooling-maps {
> + map0 {
> + trip = <&cpu_alert1>;
> + cooling-device = <&fan0 0 1>;
> + };
> + };
> + };
> +
> +for PWM FAN used as cooling device can be found at:
> +./Documentation/devicetree/bindings/thermal/thermal.txt
>
> Example:
> pwm-fan {
> compatible = "pwm-fan";
> status = "okay";
> pwms = <&pwm 0 10000 0>;
> + cooling-min-state = <0>;
> + cooling-max-state = <3>;
> + #cooling-cells = <2>;
> + cooling-pwm-values = <0 102 170 255>;
> + default-pulse-width = <0>;
> };
>
Hi Sjoerd,
Thanks for your feedback and sorry for a late reply.
> On Thu, 2014-12-18 at 11:13 +0100, Lukasz Majewski wrote:
> > Several new properties to allow PWM fan working as a cooling device
> > have been combined into this single commit.
> >
> > Signed-off-by: Lukasz Majewski <[email protected]>
> > ---
> > .../devicetree/bindings/hwmon/pwm-fan.txt | 28
> > ++++++++++++++++++++++ 1 file changed, 28 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
> > b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt index
> > 610757c..3877810 100644 ---
> > a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt +++
> > b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt @@ -3,10
> > +3,38 @@ Bindings for a fan connected to the PWM lines Required
> > properties:
> > - compatible : "pwm-fan"
> > - pwms : the PWM that is used to control the PWM fan
> > +- cooling-pwm-values : PWM duty cycle values relative to
> > + cooling-max-pwm-value correspondig to
> > + proper cooling states
> > +- default-pulse-width : Property specifying default pulse
> > width for FAN
> > + at system boot (zero to disable FAN on
> > boot).
> > + Allowed range is 0 to 255
>
> The 0..255 range seems somewhat random. Would be nicer to either use
> the approach of pwm-backlight (iotw, have the range go from the first
> to the last entry of cooling-pwm-values)
I'm OK to change the default-pulse-width to be similar to
"default-brightness-level" (as it is in
Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt)
> or simply have be the duty
> lenght in NS as entries instead of the current indirection.
I'd prefer to keep the indirection - as it is utilized in the current
pwm-fan.c driver.
>
> I assumed your cooling-pwm-values are a [0..255] range as well instead
> of nanoseconds (would be good to make that more clear)?
Your assumption is correct. cooling-pwm-values are indeed in the
[0..255] range. I will add this information in v2.
>
> Also having more consistent names would be nice.. To take
> pwm-backlight as inspiration, cooling-levels and
> default-cooling-level would make it more clear the second property
> picks a default setting from the first one.
I agree.
>
> One thing i do wonder, is having an explicit default setting useful?
> Should it not default to maximum cooling unless otherwise configured
> by either the thermal framework or sysfs ?
Enabling pan to full RPM was the default behaviour in the current
pwm-fan.c file.
To be honest, there is no need to enable fan to full RPM speed in this
board for following reasons:
1. In Odroid the FAN is optional (stacked on top of a heat sink) - very
often it is just enough to only have the heat sink.
2. Odroid has thermal enabled by default and IMHO it would be more
feasible to allow thermal to control fan from the very beginning.
However, I can also understand if the policy for hwmon implies a rule
to enable by default all fans to full RPM speed.
>
>
> > +Thorough description of the following bindings:
> > + cooling-min-state = <0>;
> > + cooling-max-state = <3>;
> > + #cooling-cells = <2>;
> > + thermal-zone {
> > + cpu_thermal: cpu-thermal {
> > + cooling-maps {
> > + map0 {
> > + trip = <&cpu_alert1>;
> > + cooling-device = <&fan0 0 1>;
> > + };
> > + };
> > + };
> > +
> > +for PWM FAN used as cooling device can be found at:
> > +./Documentation/devicetree/bindings/thermal/thermal.txt
> >
> > Example:
> > pwm-fan {
> > compatible = "pwm-fan";
> > status = "okay";
> > pwms = <&pwm 0 10000 0>;
> > + cooling-min-state = <0>;
> > + cooling-max-state = <3>;
> > + #cooling-cells = <2>;
> > + cooling-pwm-values = <0 102 170 255>;
> > + default-pulse-width = <0>;
> > };
>
>
--
Best regards,
Lukasz Majewski
Samsung R&D Institute Poland (SRPOL) | Linux Platform Group
On Fri, Dec 19, 2014 at 04:32:24PM +0100, Lukasz Majewski wrote:
> Hi Sjoerd,
>
> Thanks for your feedback and sorry for a late reply.
>
> > On Thu, 2014-12-18 at 11:13 +0100, Lukasz Majewski wrote:
> > > Several new properties to allow PWM fan working as a cooling device
> > > have been combined into this single commit.
> > >
> > > Signed-off-by: Lukasz Majewski <[email protected]>
> > > ---
> > > .../devicetree/bindings/hwmon/pwm-fan.txt | 28
> > > ++++++++++++++++++++++ 1 file changed, 28 insertions(+)
> > >
> > > diff --git a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
> > > b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt index
> > > 610757c..3877810 100644 ---
> > > a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt +++
> > > b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt @@ -3,10
> > > +3,38 @@ Bindings for a fan connected to the PWM lines Required
> > > properties:
> > > - compatible : "pwm-fan"
> > > - pwms : the PWM that is used to control the PWM fan
> > > +- cooling-pwm-values : PWM duty cycle values relative to
> > > + cooling-max-pwm-value correspondig to
> > > + proper cooling states
> > > +- default-pulse-width : Property specifying default pulse
> > > width for FAN
> > > + at system boot (zero to disable FAN on
> > > boot).
> > > + Allowed range is 0 to 255
> >
> > The 0..255 range seems somewhat random. Would be nicer to either use
> > the approach of pwm-backlight (iotw, have the range go from the first
> > to the last entry of cooling-pwm-values)
>
> I'm OK to change the default-pulse-width to be similar to
> "default-brightness-level" (as it is in
> Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt)
>
> > or simply have be the duty
> > lenght in NS as entries instead of the current indirection.
>
> I'd prefer to keep the indirection - as it is utilized in the current
> pwm-fan.c driver.
>
FWIW, devicetree information is supposed to be implementation independent.
So this is a poor argument.
>
> Enabling pan to full RPM was the default behaviour in the current
> pwm-fan.c file.
>
> To be honest, there is no need to enable fan to full RPM speed in this
> board for following reasons:
> 1. In Odroid the FAN is optional (stacked on top of a heat sink) - very
> often it is just enough to only have the heat sink.
>
> 2. Odroid has thermal enabled by default and IMHO it would be more
> feasible to allow thermal to control fan from the very beginning.
>
> However, I can also understand if the policy for hwmon implies a rule
> to enable by default all fans to full RPM speed.
>
Why and how does that all suggest that the current default behavior
should be changed ?
Guenter
Hi Guenter,
> On Fri, Dec 19, 2014 at 04:32:24PM +0100, Lukasz Majewski wrote:
> > Hi Sjoerd,
> >
> > Thanks for your feedback and sorry for a late reply.
> >
> > > On Thu, 2014-12-18 at 11:13 +0100, Lukasz Majewski wrote:
> > > > Several new properties to allow PWM fan working as a cooling
> > > > device have been combined into this single commit.
> > > >
> > > > Signed-off-by: Lukasz Majewski <[email protected]>
> > > > ---
> > > > .../devicetree/bindings/hwmon/pwm-fan.txt | 28
> > > > ++++++++++++++++++++++ 1 file changed, 28 insertions(+)
> > > >
> > > > diff --git a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
> > > > b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt index
> > > > 610757c..3877810 100644 ---
> > > > a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt +++
> > > > b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt @@ -3,10
> > > > +3,38 @@ Bindings for a fan connected to the PWM lines Required
> > > > properties:
> > > > - compatible : "pwm-fan"
> > > > - pwms : the PWM that is used to control the
> > > > PWM fan +- cooling-pwm-values : PWM duty cycle values
> > > > relative to
> > > > + cooling-max-pwm-value correspondig
> > > > to
> > > > + proper cooling states
> > > > +- default-pulse-width : Property specifying default pulse
> > > > width for FAN
> > > > + at system boot (zero to disable
> > > > FAN on boot).
> > > > + Allowed range is 0 to 255
> > >
> > > The 0..255 range seems somewhat random. Would be nicer to either
> > > use the approach of pwm-backlight (iotw, have the range go from
> > > the first to the last entry of cooling-pwm-values)
> >
> > I'm OK to change the default-pulse-width to be similar to
> > "default-brightness-level" (as it is in
> > Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt)
> >
> > > or simply have be the duty
> > > lenght in NS as entries instead of the current indirection.
> >
> > I'd prefer to keep the indirection - as it is utilized in the
> > current pwm-fan.c driver.
> >
> FWIW, devicetree information is supposed to be implementation
> independent. So this is a poor argument.
Many other pwm drivers use the indirection - e.g. mentioned
pwm-backlight.
>
> >
> > Enabling pan to full RPM was the default behaviour in the current
> > pwm-fan.c file.
> >
> > To be honest, there is no need to enable fan to full RPM speed in
> > this board for following reasons:
> > 1. In Odroid the FAN is optional (stacked on top of a heat sink) -
> > very often it is just enough to only have the heat sink.
> >
> > 2. Odroid has thermal enabled by default and IMHO it would be more
> > feasible to allow thermal to control fan from the very beginning.
> >
> > However, I can also understand if the policy for hwmon implies a
> > rule to enable by default all fans to full RPM speed.
> >
> Why and how does that all suggest that the current default behavior
> should be changed ?
I wanted to avoid the unpleasant sound for full speed fan when thermal
is not enabled by default.
But as I said, I fully understand the policy and I would be happy to
comply with it as thermal should reduce the fan speed anyway at boot
time.
>
> Guenter
--
Best regards,
Lukasz Majewski
Samsung R&D Institute Poland (SRPOL) | Linux Platform Group
Hi Guenter,
> On 12/18/2014 02:13 AM, Lukasz Majewski wrote:
> > Several new properties to allow PWM fan working as a cooling device
> > have been combined into this single commit.
> >
> > Signed-off-by: Lukasz Majewski <[email protected]>
> > ---
> > .../devicetree/bindings/hwmon/pwm-fan.txt | 28
> > ++++++++++++++++++++++ 1 file changed, 28 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
> > b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt index
> > 610757c..3877810 100644 ---
> > a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt +++
> > b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt @@ -3,10
> > +3,38 @@ Bindings for a fan connected to the PWM lines Required
> > properties:
> > - compatible : "pwm-fan"
> > - pwms : the PWM that is used to control the PWM
> > fan +- cooling-pwm-values : PWM duty cycle values relative to
> > + cooling-max-pwm-value correspondig to
> > + proper cooling states
>
> I don't understand what you mean with "relative to". Please elaborate.
> Do you mean "associated with" ?
I meant that cooling-pwm-values is no greater than
cooling-max-pwm-value (which was present in some earlier version of
this patch and had value of 255).
This description is wrong and will be reworded.
>
> Where is "cooling-max-pwm-value" defined,
It was present in some early version of this patch.
> and how is this all expected
> to relate to the maximum duty cycle value provided by the pwm device ?
>
> > +- default-pulse-width : Property specifying default pulse
> > width for FAN
> > + at system boot (zero to disable FAN on
> > boot).
> > + Allowed range is 0 to 255
>
> You'll need dt maintainer approval for the new properties.
I'm wondering how this patch series will get merged. It touches three
different subsystems (thermal, hwmon and device tree for Exynos).
For me it would be best to use thermal tree (hence Odroid U3 fan is
supposed to work as a cooling device) with ACKs from other subsystems
maintainers.
>
> One thing I wonder about though is why you use "default-pulse-width"
> and not "default-pwm". Seems to be arbitrary. I don't see
> "pulse-width" used anywhere in the upstream kernel.
Believe or not I've also considered the default-pwm name.
>
> I am somewhat concerned that you define the new properties as
> mandatory. That means existing configurations will fail, which does
> not seem to be a good idea. It would be more appropriate to not
> configure the thermal device if the new properties are not provided.
Very good point. I will do that for v2.
>
> The newly introduced semantics also conflicts with the current
> semantics, which sets the initial duty cycle initially to the maximum
> allowed duty cycle as reported by the pwm device itself.
Ok. I will stick to above policy and then the "default-pulse-width"
property can be removed.
>
> Guenter
>
> > +
> > +Thorough description of the following bindings:
> > + cooling-min-state = <0>;
> > + cooling-max-state = <3>;
> > + #cooling-cells = <2>;
> > + thermal-zone {
> > + cpu_thermal: cpu-thermal {
> > + cooling-maps {
> > + map0 {
> > + trip = <&cpu_alert1>;
> > + cooling-device = <&fan0 0 1>;
> > + };
> > + };
> > + };
> > +
> > +for PWM FAN used as cooling device can be found at:
> > +./Documentation/devicetree/bindings/thermal/thermal.txt
> >
> > Example:
> > pwm-fan {
> > compatible = "pwm-fan";
> > status = "okay";
> > pwms = <&pwm 0 10000 0>;
> > + cooling-min-state = <0>;
> > + cooling-max-state = <3>;
> > + #cooling-cells = <2>;
> > + cooling-pwm-values = <0 102 170 255>;
> > + default-pulse-width = <0>;
> > };
> >
>
--
Best regards,
Lukasz Majewski
Samsung R&D Institute Poland (SRPOL) | Linux Platform Group
Presented patches add support for Odroid's U3 optional CPU FAN, which uses PWM
subsystem for low level control.
After successful probe it registers itself as a cooling device for thermal
subsystem. To preserve the ability to use this fan as a PWM device stubs for
thermal_of_cooling_device_register() and thermal_cdev_update() have been added.
Additionally, devices without support for DTS are still supported.
To provide correct functionality, new properties to device tree description for
Exynos4412 and in particular Odroid U3 have been added.
Those patches were tested on Exynos4412 - Odroid U3 board.
Patches were applied on:
linux-soc-thermal/next branch
SHA1: d8c3cd75e77d8033b29470525781befc11b6bf7d
with following preceding patch series:
'thermal: exynos: Thermal code rework to use device tree'
http://www.spinics.net/lists/linux-samsung-soc/msg37719.html
Presented code utilize reworked Exynos thermal subsystem.
Kamil Debski (1):
ARM: dts: Add pwm-fan node to the Odroid-U3 board
Lukasz Majewski (7):
thermal: Provide stub for thermal_of_cooling_device_register()
function
thermal: Provide stub for thermal_cdev_update() function
hwmon: dts: Doc: Add DTS doc to explain how to use PWM FAN as a
cooling device
hwmon: thermal: dts: Add properties to use pwm-fan device as a cooling
device in Odroid U3
hwmon: thermal: Extract __set_pwm() function to only modify PWM duty
cycle
hwmon: thermal: Read PWM FAN configuration from device tree
hwmon: thermal: Code for using PWM FAN as a cooling device
.../devicetree/bindings/hwmon/pwm-fan.txt | 23 +++
arch/arm/boot/dts/exynos4.dtsi | 2 +-
arch/arm/boot/dts/exynos4412-odroidu3.dts | 44 ++++++
drivers/hwmon/pwm-fan.c | 166 +++++++++++++++++++--
include/linux/thermal.h | 20 ++-
5 files changed, 235 insertions(+), 20 deletions(-)
--
2.0.0.rc2
Odroid U3 fan can work without being registered as OF cooling device
(with CONFIG_THERMAL_OF disabled).
In this situation it can be controlled via PWM entry at
/sys/class/hwmon/hwmon0/pwm1.
Therefore, the thermal_of_cooling_device_register() function needs a stub
to allow clean compilation.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- None
---
include/linux/thermal.h | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 2de3d9e..871123c 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -328,6 +328,10 @@ thermal_zone_of_sensor_register(struct device *dev, int id, void *data,
const struct thermal_zone_of_device_ops *ops);
void thermal_zone_of_sensor_unregister(struct device *dev,
struct thermal_zone_device *tz);
+struct thermal_cooling_device *
+thermal_of_cooling_device_register(struct device_node *np,
+ char *type, void *devdata,
+ const struct thermal_cooling_device_ops *);
#else
static inline struct thermal_zone_device *
thermal_zone_of_sensor_register(struct device *dev, int id, void *data,
@@ -342,6 +346,13 @@ void thermal_zone_of_sensor_unregister(struct device *dev,
{
}
+static inline struct thermal_cooling_device *
+thermal_of_cooling_device_register(struct device_node *np,
+ char *type, void *devdata,
+ const struct thermal_cooling_device_ops *ops)
+{
+ return NULL;
+}
#endif
struct thermal_zone_device *thermal_zone_device_register(const char *, int, int,
void *, struct thermal_zone_device_ops *,
@@ -357,9 +368,6 @@ void thermal_zone_device_update(struct thermal_zone_device *);
struct thermal_cooling_device *thermal_cooling_device_register(char *, void *,
const struct thermal_cooling_device_ops *);
-struct thermal_cooling_device *
-thermal_of_cooling_device_register(struct device_node *np, char *, void *,
- const struct thermal_cooling_device_ops *);
void thermal_cooling_device_unregister(struct thermal_cooling_device *);
struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name);
int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp);
--
2.0.0.rc2
Odroid U3 fan can work without being registered as OF cooling device
(with CONFIG_THERMAL_OF disabled).
In this situation it can be controlled via PWM entry at
/sys/class/hwmon/hwmon0/pwm1.
Therefore, the thermal_cdev_update() function needs a stub
to allow clean compilation.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- New patch
---
include/linux/thermal.h | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 871123c..b3515b5 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -332,6 +332,7 @@ struct thermal_cooling_device *
thermal_of_cooling_device_register(struct device_node *np,
char *type, void *devdata,
const struct thermal_cooling_device_ops *);
+void thermal_cdev_update(struct thermal_cooling_device *);
#else
static inline struct thermal_zone_device *
thermal_zone_of_sensor_register(struct device *dev, int id, void *data,
@@ -353,6 +354,10 @@ thermal_of_cooling_device_register(struct device_node *np,
{
return NULL;
}
+
+static inline void thermal_cdev_update(struct thermal_cooling_device *cdev)
+{
+}
#endif
struct thermal_zone_device *thermal_zone_device_register(const char *, int, int,
void *, struct thermal_zone_device_ops *,
@@ -375,7 +380,6 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp);
int get_tz_trend(struct thermal_zone_device *, int);
struct thermal_instance *get_thermal_instance(struct thermal_zone_device *,
struct thermal_cooling_device *, int);
-void thermal_cdev_update(struct thermal_cooling_device *);
void thermal_notify_framework(struct thermal_zone_device *, int);
#ifdef CONFIG_NET
--
2.0.0.rc2
Several new properties to allow PWM fan working as a cooling device have been
combined into this single commit.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- Rename cooling-pwm-values to cooling-levels
- Remove default-pulse-width property and stick to default hwmon policy
---
.../devicetree/bindings/hwmon/pwm-fan.txt | 23 ++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
index 610757c..001446a 100644
--- a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
+++ b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
@@ -3,10 +3,33 @@ Bindings for a fan connected to the PWM lines
Required properties:
- compatible : "pwm-fan"
- pwms : the PWM that is used to control the PWM fan
+- cooling-levels : PWM duty cycle values in a range from 0 to 255
+ which correspond to thermal cooling states
+
+Thorough description of the following bindings:
+ cooling-min-state = <0>;
+ cooling-max-state = <3>;
+ #cooling-cells = <2>;
+ thermal-zone {
+ cpu_thermal: cpu-thermal {
+ cooling-maps {
+ map0 {
+ trip = <&cpu_alert1>;
+ cooling-device = <&fan0 0 1>;
+ };
+ };
+ };
+
+for PWM FAN used as cooling device can be found at:
+./Documentation/devicetree/bindings/thermal/thermal.txt
Example:
pwm-fan {
compatible = "pwm-fan";
status = "okay";
pwms = <&pwm 0 10000 0>;
+ cooling-min-state = <0>;
+ cooling-max-state = <3>;
+ #cooling-cells = <2>;
+ cooling-levels = <0 102 170 255>;
};
--
2.0.0.rc2
From: Kamil Debski <[email protected]>
Add pwm-fan node to the Odroid-U3 board file to enable PWM control of the
cooling fan. In addition, add the "pwm" label to the pwm@139D0000 node
in the exynos4412.dtsi.
Signed-off-by: Kamil Debski <[email protected]>
[Rebased on the newest mainline by [email protected]]
---
Changes since v1:
- added pwm label to the pwm@139D0000 node in exynos4.dtsi
- use the pwm label in the exynos4412-odroidu3.dts
- change order or properties in the pwn-fan node, to be sorted
in alphabetical order
---
arch/arm/boot/dts/exynos4.dtsi | 2 +-
arch/arm/boot/dts/exynos4412-odroidu3.dts | 13 +++++++++++++
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi
index 1735bb3..f227a39 100644
--- a/arch/arm/boot/dts/exynos4.dtsi
+++ b/arch/arm/boot/dts/exynos4.dtsi
@@ -583,7 +583,7 @@
status = "disabled";
};
- pwm@139D0000 {
+ pwm: pwm@139D0000 {
compatible = "samsung,exynos4210-pwm";
reg = <0x139D0000 0x1000>;
interrupts = <0 37 0>, <0 38 0>, <0 39 0>, <0 40 0>, <0 41 0>;
diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts
index c8a64be..60bd1e4 100644
--- a/arch/arm/boot/dts/exynos4412-odroidu3.dts
+++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts
@@ -31,6 +31,19 @@
linux,default-trigger = "heartbeat";
};
};
+
+ pwm-fan {
+ compatible = "pwm-fan";
+ pwms = <&pwm 0 10000 0>;
+ status = "okay";
+ };
+};
+
+&pwm {
+ pinctrl-0 = <&pwm0_out>;
+ pinctrl-names = "default";
+ samsung,pwm-outputs = <0>;
+ status = "okay";
};
&usb3503 {
--
2.0.0.rc2
With those bindings it is possible to use pwm-fan device available in
Odroid U3 as a cooling device.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- Rename cooling-pwm-values property to cooling-levels
---
arch/arm/boot/dts/exynos4412-odroidu3.dts | 33 ++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts
index 60bd1e4..380035d 100644
--- a/arch/arm/boot/dts/exynos4412-odroidu3.dts
+++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts
@@ -32,11 +32,42 @@
};
};
- pwm-fan {
+ fan0: pwm-fan {
compatible = "pwm-fan";
pwms = <&pwm 0 10000 0>;
+ cooling-min-state = <0>;
+ cooling-max-state = <3>;
+ #cooling-cells = <2>;
+ cooling-levels = <0 102 170 255>;
status = "okay";
};
+
+ thermal-zones {
+ cpu_thermal: cpu-thermal {
+ cooling-maps {
+ map0 {
+ trip = <&cpu_alert1>;
+ cooling-device = <&cpu0 7 7>;
+ };
+ map1 {
+ trip = <&cpu_alert2>;
+ cooling-device = <&cpu0 13 13>;
+ };
+ map2 {
+ trip = <&cpu_alert0>;
+ cooling-device = <&fan0 0 1>;
+ };
+ map3 {
+ trip = <&cpu_alert1>;
+ cooling-device = <&fan0 1 2>;
+ };
+ map4 {
+ trip = <&cpu_alert2>;
+ cooling-device = <&fan0 2 3>;
+ };
+ };
+ };
+ };
};
&pwm {
--
2.0.0.rc2
Code for reading PWM FAN configuration data via device tree.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- Rename pwm_fan_max_states to pwm_fan_cooling_levels
- Moving pwm_fan_of_get_cooling_data() call after setting end enabling PWM FAN
- pwm_fan_of_get_cooling_data() now can fail - preserving old behaviour
- Remove unnecessary dev_err() call
---
drivers/hwmon/pwm-fan.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 47 insertions(+), 1 deletion(-)
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 870e100..8e68308 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -30,7 +30,10 @@
struct pwm_fan_ctx {
struct mutex lock;
struct pwm_device *pwm;
- unsigned char pwm_value;
+ unsigned int pwm_value;
+ unsigned int pwm_fan_state;
+ unsigned int pwm_fan_max_state;
+ unsigned int *pwm_fan_cooling_levels;
};
static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
@@ -100,6 +103,47 @@ static struct attribute *pwm_fan_attrs[] = {
ATTRIBUTE_GROUPS(pwm_fan);
+int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
+{
+ struct device_node *np = dev->of_node;
+ struct property *pp;
+ int len, num, i;
+
+ pp = of_find_property(np, "cooling-levels", &len);
+ if (!pp) {
+ dev_err(dev, "Property: 'cooling-levels' not found\n");
+ return -EINVAL;
+ }
+
+ if (len == 0) {
+ dev_err(dev, "Length wrong value!\n");
+ return -EINVAL;
+ }
+
+ ctx->pwm_fan_cooling_levels = devm_kzalloc(dev, len, GFP_KERNEL);
+ if (!ctx->pwm_fan_cooling_levels)
+ return -ENOMEM;
+
+ num = len / sizeof(u32);
+ if (of_property_read_u32_array(np, pp->name,
+ ctx->pwm_fan_cooling_levels, num)) {
+ dev_err(dev, "Property: %s cannot be read!\n", pp->name);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < num; i++) {
+ if (ctx->pwm_fan_cooling_levels[i] > MAX_PWM) {
+ dev_err(dev, "PWM fan state[%d]:%d > %d\n", i,
+ ctx->pwm_fan_cooling_levels[i], MAX_PWM);
+ return -EINVAL;
+ }
+ }
+
+ ctx->pwm_fan_max_state = num - 1;
+
+ return 0;
+}
+
static int pwm_fan_probe(struct platform_device *pdev)
{
struct device *hwmon;
@@ -145,6 +189,8 @@ static int pwm_fan_probe(struct platform_device *pdev)
pwm_disable(ctx->pwm);
return PTR_ERR(hwmon);
}
+
+ pwm_fan_of_get_cooling_data(&pdev->dev, ctx);
return 0;
}
--
2.0.0.rc2
The PWM FAN device can now be used as a thermal cooling device. Necessary
infrastructure has been added in this commit.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- Replace pwm_fan_cooling_states with pwm_fan_cooling_levels
- Update ctx->pwm_fan_state when correct data from device tree is available
- Using therma_cdev_update() when thermal is ready for controlling the fan
---
drivers/hwmon/pwm-fan.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 83 insertions(+), 2 deletions(-)
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 8e68308..a47fc3c 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -24,6 +24,7 @@
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/sysfs.h>
+#include <linux/thermal.h>
#define MAX_PWM 255
@@ -68,6 +69,17 @@ exit_set_pwm_err:
return ret;
}
+static void pwm_fan_update_state(struct pwm_fan_ctx *ctx, unsigned long pwm)
+{
+ int i;
+
+ for (i = 0; i < ctx->pwm_fan_max_state; ++i)
+ if (pwm < ctx->pwm_fan_cooling_levels[i + 1])
+ break;
+
+ ctx->pwm_fan_state = i;
+}
+
static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -82,6 +94,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
if (ret)
return ret;
+ pwm_fan_update_state(ctx, pwm);
return count;
}
@@ -103,6 +116,59 @@ static struct attribute *pwm_fan_attrs[] = {
ATTRIBUTE_GROUPS(pwm_fan);
+/* thermal cooling device callbacks */
+static int pwm_fan_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+
+ *state = ctx->pwm_fan_max_state;
+
+ return 0;
+}
+
+static int pwm_fan_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+
+ if (!ctx)
+ return -EINVAL;
+
+ *state = ctx->pwm_fan_state;
+
+ return 0;
+}
+
+static int
+pwm_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+ int ret;
+
+ if (!ctx || (state > ctx->pwm_fan_max_state))
+ return -EINVAL;
+
+ if (state == ctx->pwm_fan_state)
+ return 0;
+
+ ret = __set_pwm(ctx, ctx->pwm_fan_cooling_levels[state]);
+ if (ret) {
+ dev_err(&cdev->device, "Cannot set pwm!\n");
+ return ret;
+ }
+
+ ctx->pwm_fan_state = state;
+
+ return ret;
+}
+
+static const struct thermal_cooling_device_ops pwm_fan_cooling_ops = {
+ .get_max_state = pwm_fan_get_max_state,
+ .get_cur_state = pwm_fan_get_cur_state,
+ .set_cur_state = pwm_fan_set_cur_state,
+};
+
int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
{
struct device_node *np = dev->of_node;
@@ -146,8 +212,9 @@ int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
static int pwm_fan_probe(struct platform_device *pdev)
{
- struct device *hwmon;
+ struct thermal_cooling_device *cdev;
struct pwm_fan_ctx *ctx;
+ struct device *hwmon;
int duty_cycle;
int ret;
@@ -190,7 +257,21 @@ static int pwm_fan_probe(struct platform_device *pdev)
return PTR_ERR(hwmon);
}
- pwm_fan_of_get_cooling_data(&pdev->dev, ctx);
+ ret = pwm_fan_of_get_cooling_data(&pdev->dev, ctx);
+ if (!ret) {
+ ctx->pwm_fan_state = ctx->pwm_fan_max_state;
+ cdev = thermal_of_cooling_device_register(pdev->dev.of_node,
+ "pwm-fan", ctx,
+ &pwm_fan_cooling_ops);
+ if (IS_ERR(cdev)) {
+ dev_err(&pdev->dev,
+ "Failed to register pwm-fan as cooling device");
+ pwm_disable(ctx->pwm);
+ return PTR_ERR(cdev);
+ }
+ thermal_cdev_update(cdev);
+ }
+
return 0;
}
--
2.0.0.rc2
It was necessary to decouple code handling writing to sysfs from the one
responsible for setting PWM of the fan.
Due to that, new __set_pwm() method was extracted, which is responsible for
only setting new PWM duty cycle.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- None
---
drivers/hwmon/pwm-fan.c | 35 ++++++++++++++++++++++-------------
1 file changed, 22 insertions(+), 13 deletions(-)
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 1991d903..870e100 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -33,21 +33,15 @@ struct pwm_fan_ctx {
unsigned char pwm_value;
};
-static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
{
- struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
- unsigned long pwm, duty;
- ssize_t ret;
-
- if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
- return -EINVAL;
-
- mutex_lock(&ctx->lock);
+ unsigned long duty;
+ int ret;
if (ctx->pwm_value == pwm)
- goto exit_set_pwm_no_change;
+ return 0;
+ mutex_lock(&ctx->lock);
if (pwm == 0) {
pwm_disable(ctx->pwm);
goto exit_set_pwm;
@@ -66,13 +60,28 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
exit_set_pwm:
ctx->pwm_value = pwm;
-exit_set_pwm_no_change:
- ret = count;
exit_set_pwm_err:
mutex_unlock(&ctx->lock);
return ret;
}
+static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
+ unsigned long pwm;
+ int ret;
+
+ if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
+ return -EINVAL;
+
+ ret = __set_pwm(ctx, pwm);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
static ssize_t show_pwm(struct device *dev,
struct device_attribute *attr, char *buf)
{
--
2.0.0.rc2
On Mon, Dec 22, 2014 at 05:27:47PM +0100, Lukasz Majewski wrote:
> Code for reading PWM FAN configuration data via device tree.
>
> Signed-off-by: Lukasz Majewski <[email protected]>
> ---
The headline is quite misleading. Please provide the affected subsystem (hwmon)
and the affected driver (pwm-fan) in the hwmon-customary form
hwmon: (pwm-fan) Description
The Description should explain what the patch is about.
> Changes for v2:
> - Rename pwm_fan_max_states to pwm_fan_cooling_levels
> - Moving pwm_fan_of_get_cooling_data() call after setting end enabling PWM FAN
> - pwm_fan_of_get_cooling_data() now can fail - preserving old behaviour
> - Remove unnecessary dev_err() call
> ---
> drivers/hwmon/pwm-fan.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 47 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
> index 870e100..8e68308 100644
> --- a/drivers/hwmon/pwm-fan.c
> +++ b/drivers/hwmon/pwm-fan.c
> @@ -30,7 +30,10 @@
> struct pwm_fan_ctx {
> struct mutex lock;
> struct pwm_device *pwm;
> - unsigned char pwm_value;
> + unsigned int pwm_value;
> + unsigned int pwm_fan_state;
> + unsigned int pwm_fan_max_state;
> + unsigned int *pwm_fan_cooling_levels;
> };
>
> static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
> @@ -100,6 +103,47 @@ static struct attribute *pwm_fan_attrs[] = {
>
> ATTRIBUTE_GROUPS(pwm_fan);
>
> +int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
> +{
> + struct device_node *np = dev->of_node;
> + struct property *pp;
> + int len, num, i;
> +
> + pp = of_find_property(np, "cooling-levels", &len);
> + if (!pp) {
> + dev_err(dev, "Property: 'cooling-levels' not found\n");
> + return -EINVAL;
> + }
> +
> + if (len == 0) {
> + dev_err(dev, "Length wrong value!\n");
Semantics. "Wrong length" would be better.
> + return -EINVAL;
> + }
> +
of_property_count_elems_of_size() might be more appropriate here.
> + ctx->pwm_fan_cooling_levels = devm_kzalloc(dev, len, GFP_KERNEL);
> + if (!ctx->pwm_fan_cooling_levels)
> + return -ENOMEM;
> +
> + num = len / sizeof(u32);
What if 'num' is 0 ? Is that guaranteed to never happen ?
> + if (of_property_read_u32_array(np, pp->name,
> + ctx->pwm_fan_cooling_levels, num)) {
> + dev_err(dev, "Property: %s cannot be read!\n", pp->name);
The ':' after 'Property' in those error messages doesn't seem to make much sense
to me.
> + return -EINVAL;
of_property_read_u32_array() returns an error code. Please use it.
> + }
> +
> + for (i = 0; i < num; i++) {
> + if (ctx->pwm_fan_cooling_levels[i] > MAX_PWM) {
> + dev_err(dev, "PWM fan state[%d]:%d > %d\n", i,
> + ctx->pwm_fan_cooling_levels[i], MAX_PWM);
> + return -EINVAL;
> + }
> + }
> +
> + ctx->pwm_fan_max_state = num - 1;
> +
> + return 0;
> +}
> +
> static int pwm_fan_probe(struct platform_device *pdev)
> {
> struct device *hwmon;
> @@ -145,6 +189,8 @@ static int pwm_fan_probe(struct platform_device *pdev)
> pwm_disable(ctx->pwm);
> return PTR_ERR(hwmon);
> }
> +
> + pwm_fan_of_get_cooling_data(&pdev->dev, ctx);
Now this is odd. Adding a function to return an error code just to ignore it
doesn't make much sense. While it makes sense to ignore errors if there is no
cooling data in the devicetree, errors due to bad devicetree data should not be
ignored.
I am also a bit concerned that you make this call _after_ instantiating the
hwmon device; this may potentially result in a race condition. Please ensure
that this is not the case.
Thanks,
Guenter
On Mon, Dec 22, 2014 at 05:27:46PM +0100, Lukasz Majewski wrote:
> It was necessary to decouple code handling writing to sysfs from the one
> responsible for setting PWM of the fan.
> Due to that, new __set_pwm() method was extracted, which is responsible for
> only setting new PWM duty cycle.
>
> Signed-off-by: Lukasz Majewski <[email protected]>
> ---
Please provide the affected subsystem and the affected driver in the header.
While it may make sense to explain that the patch is to prepare the driver for
thermal use, this should be part of the descriptive text. The patch is,
however, not not a thermal subsystem related patch. The 'thermal:' in the
headline is thus misleading, and you should drop it.
Guenter
Presented patches add support for Odroid's U3 optional CPU FAN, which uses PWM
subsystem for low level control.
After successful probe it registers itself as a cooling device for thermal
subsystem. To preserve the ability to use this fan as a PWM device stubs for
thermal_of_cooling_device_register() and thermal_cdev_update() have been added.
Additionally, devices without support for DTS are still supported.
To provide correct functionality, new properties to device tree description for
Exynos4412 and in particular Odroid U3 have been added.
Those patches were tested on Exynos4412 - Odroid U3 board.
Patches were applied on:
linux-soc-thermal/next branch (Linux 3.19-rc5)
SHA1: 252454f5cbda2c6b40c5d36f58cac2938437b85d
Kamil Debski (1):
ARM: dts: Add pwm-fan node to the Odroid-U3 board
Lukasz Majewski (7):
thermal: Provide stub for thermal_of_cooling_device_register()
function
thermal: Provide stub for thermal_cdev_update() function
Documentation: dts: Documentation entry to explain how to use PWM FAN
as a cooling device
ARM: dts: Add properties to use pwm-fan device as a cooling device in
Odroid U3
hwmon: pwm-fan: Extract __set_pwm() function to only modify PWM duty
cycle
hwmon: pwm-fan: Read PWM FAN configuration from device tree
hwmon: pwm-fan: Code for using PWM FAN as a cooling device
.../devicetree/bindings/hwmon/pwm-fan.txt | 20 +++
arch/arm/boot/dts/exynos4.dtsi | 2 +-
arch/arm/boot/dts/exynos4412-odroidu3.dts | 44 ++++++
drivers/hwmon/pwm-fan.c | 171 +++++++++++++++++++--
include/linux/thermal.h | 22 ++-
5 files changed, 239 insertions(+), 20 deletions(-)
--
2.0.0.rc2
Odroid U3 fan can work without being registered as OF cooling device
(with CONFIG_THERMAL_OF disabled).
In this situation it can be controlled via PWM entry at
/sys/class/hwmon/hwmon0/pwm1.
Therefore, the thermal_of_cooling_device_register() function needs a stub
to allow clean compilation.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- None
Changes for v3:
- Provide stub declaration when CONFIG_THERMAL is not set
---
include/linux/thermal.h | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index fc52e30..eacf2de 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -293,6 +293,20 @@ struct thermal_trip {
};
/* Function declarations */
+#ifdef CONFIG_THERMAL
+struct thermal_cooling_device *
+thermal_of_cooling_device_register(struct device_node *np,
+ char *type, void *devdata,
+ const struct thermal_cooling_device_ops *);
+#else
+static inline struct thermal_cooling_device *
+thermal_of_cooling_device_register(struct device_node *np,
+ char *type, void *devdata,
+ const struct thermal_cooling_device_ops *ops)
+{
+ return NULL;
+}
+#endif
#ifdef CONFIG_THERMAL_OF
struct thermal_zone_device *
thermal_zone_of_sensor_register(struct device *dev, int id, void *data,
@@ -328,9 +342,6 @@ void thermal_zone_device_update(struct thermal_zone_device *);
struct thermal_cooling_device *thermal_cooling_device_register(char *, void *,
const struct thermal_cooling_device_ops *);
-struct thermal_cooling_device *
-thermal_of_cooling_device_register(struct device_node *np, char *, void *,
- const struct thermal_cooling_device_ops *);
void thermal_cooling_device_unregister(struct thermal_cooling_device *);
struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name);
int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp);
--
2.0.0.rc2
Odroid U3 fan can work without being registered as OF cooling device
(with CONFIG_THERMAL{_OF|} disabled).
In this situation it can be controlled via PWM entry at
/sys/class/hwmon/hwmon0/pwm1.
Therefore, the thermal_cdev_update() function needs a stub
to allow clean compilation.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- New patch
Changes for v3:
- thermal_cdev_update() now depends on CONFIG_THERMAL flag
---
include/linux/thermal.h | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index eacf2de..25382e6 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -298,6 +298,7 @@ struct thermal_cooling_device *
thermal_of_cooling_device_register(struct device_node *np,
char *type, void *devdata,
const struct thermal_cooling_device_ops *);
+void thermal_cdev_update(struct thermal_cooling_device *);
#else
static inline struct thermal_cooling_device *
thermal_of_cooling_device_register(struct device_node *np,
@@ -306,6 +307,9 @@ thermal_of_cooling_device_register(struct device_node *np,
{
return NULL;
}
+static inline void thermal_cdev_update(struct thermal_cooling_device *cdev)
+{
+}
#endif
#ifdef CONFIG_THERMAL_OF
struct thermal_zone_device *
@@ -349,7 +353,6 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp);
int get_tz_trend(struct thermal_zone_device *, int);
struct thermal_instance *get_thermal_instance(struct thermal_zone_device *,
struct thermal_cooling_device *, int);
-void thermal_cdev_update(struct thermal_cooling_device *);
void thermal_notify_framework(struct thermal_zone_device *, int);
#ifdef CONFIG_NET
--
2.0.0.rc2
Explanation of several properties, which allow PWM fan working as a cooling
device, have been embraced in this commit.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- Rename cooling-pwm-values to cooling-levels
- Remove default-pulse-width property and stick to default hwmon policy
Changes for v3:
- Changing commit title from "hwmon: dts: Doc:" to "Documentation: dts"
- Remove unnecessary properties
- Set maximal cooling level to 230 instead of 255
---
Documentation/devicetree/bindings/hwmon/pwm-fan.txt | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
index 610757c..d53fe0c 100644
--- a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
+++ b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
@@ -3,10 +3,30 @@ Bindings for a fan connected to the PWM lines
Required properties:
- compatible : "pwm-fan"
- pwms : the PWM that is used to control the PWM fan
+- cooling-levels : PWM duty cycle values in a range from 0 to 255
+ which correspond to thermal cooling states
+
+Thorough description of the following bindings:
+ cooling-min-state = <0>;
+ cooling-max-state = <3>;
+ #cooling-cells = <2>;
+ thermal-zone {
+ cpu_thermal: cpu-thermal {
+ cooling-maps {
+ map0 {
+ trip = <&cpu_alert1>;
+ cooling-device = <&fan0 0 1>;
+ };
+ };
+ };
+
+for PWM FAN used as cooling device can be found at:
+./Documentation/devicetree/bindings/thermal/thermal.txt
Example:
pwm-fan {
compatible = "pwm-fan";
status = "okay";
pwms = <&pwm 0 10000 0>;
+ cooling-levels = <0 102 170 230>;
};
--
2.0.0.rc2
From: Kamil Debski <[email protected]>
Add pwm-fan node to the Odroid-U3 board file to enable PWM control of the
cooling fan. In addition, add the "pwm" label to the pwm@139D0000 node
in the exynos4412.dtsi.
Signed-off-by: Kamil Debski <[email protected]>
[Rebased on the newest mainline by [email protected]]
---
Changes since v1:
- added pwm label to the pwm@139D0000 node in exynos4.dtsi
- use the pwm label in the exynos4412-odroidu3.dts
- change order or properties in the pwn-fan node, to be sorted
in alphabetical order
---
arch/arm/boot/dts/exynos4.dtsi | 2 +-
arch/arm/boot/dts/exynos4412-odroidu3.dts | 13 +++++++++++++
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi
index f18d746..75266e3 100644
--- a/arch/arm/boot/dts/exynos4.dtsi
+++ b/arch/arm/boot/dts/exynos4.dtsi
@@ -582,7 +582,7 @@
status = "disabled";
};
- pwm@139D0000 {
+ pwm: pwm@139D0000 {
compatible = "samsung,exynos4210-pwm";
reg = <0x139D0000 0x1000>;
interrupts = <0 37 0>, <0 38 0>, <0 39 0>, <0 40 0>, <0 41 0>;
diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts
index c8a64be..60bd1e4 100644
--- a/arch/arm/boot/dts/exynos4412-odroidu3.dts
+++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts
@@ -31,6 +31,19 @@
linux,default-trigger = "heartbeat";
};
};
+
+ pwm-fan {
+ compatible = "pwm-fan";
+ pwms = <&pwm 0 10000 0>;
+ status = "okay";
+ };
+};
+
+&pwm {
+ pinctrl-0 = <&pwm0_out>;
+ pinctrl-names = "default";
+ samsung,pwm-outputs = <0>;
+ status = "okay";
};
&usb3503 {
--
2.0.0.rc2
With those bindings it is possible to use pwm-fan device available in
Odroid U3 as a cooling device.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- Rename cooling-pwm-values property to cooling-levels
Changes for v3:
- Change patch's topic to "ARM dts"
- Reduce maximal cooling-level to 230 from 255
---
arch/arm/boot/dts/exynos4412-odroidu3.dts | 33 ++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts
index 60bd1e4..3e5df3e 100644
--- a/arch/arm/boot/dts/exynos4412-odroidu3.dts
+++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts
@@ -32,11 +32,42 @@
};
};
- pwm-fan {
+ fan0: pwm-fan {
compatible = "pwm-fan";
pwms = <&pwm 0 10000 0>;
+ cooling-min-state = <0>;
+ cooling-max-state = <3>;
+ #cooling-cells = <2>;
+ cooling-levels = <0 102 170 230>;
status = "okay";
};
+
+ thermal-zones {
+ cpu_thermal: cpu-thermal {
+ cooling-maps {
+ map0 {
+ trip = <&cpu_alert1>;
+ cooling-device = <&cpu0 7 7>;
+ };
+ map1 {
+ trip = <&cpu_alert2>;
+ cooling-device = <&cpu0 13 13>;
+ };
+ map2 {
+ trip = <&cpu_alert0>;
+ cooling-device = <&fan0 0 1>;
+ };
+ map3 {
+ trip = <&cpu_alert1>;
+ cooling-device = <&fan0 1 2>;
+ };
+ map4 {
+ trip = <&cpu_alert2>;
+ cooling-device = <&fan0 2 3>;
+ };
+ };
+ };
+ };
};
&pwm {
--
2.0.0.rc2
It was necessary to decouple code handling writing to sysfs from the one
responsible for setting PWM of the fan.
Due to that, new __set_pwm() method was extracted, which is responsible for
only setting new PWM duty cycle.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- None
Changes for v3:
- The commit headline has been reedited.
---
drivers/hwmon/pwm-fan.c | 35 ++++++++++++++++++++++-------------
1 file changed, 22 insertions(+), 13 deletions(-)
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 1991d903..870e100 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -33,21 +33,15 @@ struct pwm_fan_ctx {
unsigned char pwm_value;
};
-static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
{
- struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
- unsigned long pwm, duty;
- ssize_t ret;
-
- if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
- return -EINVAL;
-
- mutex_lock(&ctx->lock);
+ unsigned long duty;
+ int ret;
if (ctx->pwm_value == pwm)
- goto exit_set_pwm_no_change;
+ return 0;
+ mutex_lock(&ctx->lock);
if (pwm == 0) {
pwm_disable(ctx->pwm);
goto exit_set_pwm;
@@ -66,13 +60,28 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
exit_set_pwm:
ctx->pwm_value = pwm;
-exit_set_pwm_no_change:
- ret = count;
exit_set_pwm_err:
mutex_unlock(&ctx->lock);
return ret;
}
+static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
+ unsigned long pwm;
+ int ret;
+
+ if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
+ return -EINVAL;
+
+ ret = __set_pwm(ctx, pwm);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
static ssize_t show_pwm(struct device *dev,
struct device_attribute *attr, char *buf)
{
--
2.0.0.rc2
This patch provides code for reading PWM FAN configuration data via
device tree. The pwm-fan can work with full speed when configuration
is not provided. However, errors are propagated when wrong DT bindings
are found.
Additionally the struct pwm_fan_ctx has been extended.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- Rename pwm_fan_max_states to pwm_fan_cooling_levels
- Moving pwm_fan_of_get_cooling_data() call after setting end enabling PWM FAN
- pwm_fan_of_get_cooling_data() now can fail - preserving old behaviour
- Remove unnecessary dev_err() call
Changes for v3:
- Patch's headline has been reedited
- pwm_fan_of_get_cooling_data() return code is now being checked.
- of_property_count_elems_of_size() is now used instead of_find_property()
- More verbose patch description added
---
drivers/hwmon/pwm-fan.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 53 insertions(+), 1 deletion(-)
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 870e100..f3f5843 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -30,7 +30,10 @@
struct pwm_fan_ctx {
struct mutex lock;
struct pwm_device *pwm;
- unsigned char pwm_value;
+ unsigned int pwm_value;
+ unsigned int pwm_fan_state;
+ unsigned int pwm_fan_max_state;
+ unsigned int *pwm_fan_cooling_levels;
};
static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
@@ -100,6 +103,50 @@ static struct attribute *pwm_fan_attrs[] = {
ATTRIBUTE_GROUPS(pwm_fan);
+int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
+{
+ struct device_node *np = dev->of_node;
+ int num, i, ret;
+
+ ret = of_property_count_elems_of_size(np, "cooling-levels",
+ sizeof(u32));
+
+ if (ret == -EINVAL) {
+ dev_err(dev, "Property 'cooling-levels' not found\n");
+ return 0;
+ }
+
+ if (ret <= 0) {
+ dev_err(dev, "Wrong data!\n");
+ return ret;
+ }
+
+ num = ret;
+ ctx->pwm_fan_cooling_levels = devm_kzalloc(dev, num * sizeof(u32),
+ GFP_KERNEL);
+ if (!ctx->pwm_fan_cooling_levels)
+ return -ENOMEM;
+
+ ret = of_property_read_u32_array(np, "cooling-levels",
+ ctx->pwm_fan_cooling_levels, num);
+ if (ret) {
+ dev_err(dev, "Property 'cooling-levels' cannot be read!\n");
+ return ret;
+ }
+
+ for (i = 0; i < num; i++) {
+ if (ctx->pwm_fan_cooling_levels[i] > MAX_PWM) {
+ dev_err(dev, "PWM fan state[%d]:%d > %d\n", i,
+ ctx->pwm_fan_cooling_levels[i], MAX_PWM);
+ return -EINVAL;
+ }
+ }
+
+ ctx->pwm_fan_max_state = num - 1;
+
+ return 0;
+}
+
static int pwm_fan_probe(struct platform_device *pdev)
{
struct device *hwmon;
@@ -145,6 +192,11 @@ static int pwm_fan_probe(struct platform_device *pdev)
pwm_disable(ctx->pwm);
return PTR_ERR(hwmon);
}
+
+ ret = pwm_fan_of_get_cooling_data(&pdev->dev, ctx);
+ if (ret)
+ return ret;
+
return 0;
}
--
2.0.0.rc2
The PWM FAN device can now be used as a thermal cooling device. Necessary
infrastructure has been added in this commit.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- Replace pwm_fan_cooling_states with pwm_fan_cooling_levels
- Update ctx->pwm_fan_state when correct data from device tree is available
- Using therma_cdev_update() when thermal is ready for controlling the fan
Changes for v3:
- Rename patch heading
- pwm_fan_of_get_cooling_data() now returns -EINVAL when no "cooling-levels"
property defined
- register of cooling device only when proper cooling data is present
---
drivers/hwmon/pwm-fan.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 83 insertions(+), 3 deletions(-)
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index f3f5843..79fe24e 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -24,6 +24,7 @@
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/sysfs.h>
+#include <linux/thermal.h>
#define MAX_PWM 255
@@ -68,6 +69,17 @@ exit_set_pwm_err:
return ret;
}
+static void pwm_fan_update_state(struct pwm_fan_ctx *ctx, unsigned long pwm)
+{
+ int i;
+
+ for (i = 0; i < ctx->pwm_fan_max_state; ++i)
+ if (pwm < ctx->pwm_fan_cooling_levels[i + 1])
+ break;
+
+ ctx->pwm_fan_state = i;
+}
+
static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -82,6 +94,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
if (ret)
return ret;
+ pwm_fan_update_state(ctx, pwm);
return count;
}
@@ -103,6 +116,59 @@ static struct attribute *pwm_fan_attrs[] = {
ATTRIBUTE_GROUPS(pwm_fan);
+/* thermal cooling device callbacks */
+static int pwm_fan_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+
+ *state = ctx->pwm_fan_max_state;
+
+ return 0;
+}
+
+static int pwm_fan_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+
+ if (!ctx)
+ return -EINVAL;
+
+ *state = ctx->pwm_fan_state;
+
+ return 0;
+}
+
+static int
+pwm_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+ int ret;
+
+ if (!ctx || (state > ctx->pwm_fan_max_state))
+ return -EINVAL;
+
+ if (state == ctx->pwm_fan_state)
+ return 0;
+
+ ret = __set_pwm(ctx, ctx->pwm_fan_cooling_levels[state]);
+ if (ret) {
+ dev_err(&cdev->device, "Cannot set pwm!\n");
+ return ret;
+ }
+
+ ctx->pwm_fan_state = state;
+
+ return ret;
+}
+
+static const struct thermal_cooling_device_ops pwm_fan_cooling_ops = {
+ .get_max_state = pwm_fan_get_max_state,
+ .get_cur_state = pwm_fan_get_cur_state,
+ .set_cur_state = pwm_fan_set_cur_state,
+};
+
int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
{
struct device_node *np = dev->of_node;
@@ -113,7 +179,7 @@ int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
if (ret == -EINVAL) {
dev_err(dev, "Property 'cooling-levels' not found\n");
- return 0;
+ return ret;
}
if (ret <= 0) {
@@ -149,8 +215,9 @@ int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
static int pwm_fan_probe(struct platform_device *pdev)
{
- struct device *hwmon;
+ struct thermal_cooling_device *cdev;
struct pwm_fan_ctx *ctx;
+ struct device *hwmon;
int duty_cycle;
int ret;
@@ -194,8 +261,21 @@ static int pwm_fan_probe(struct platform_device *pdev)
}
ret = pwm_fan_of_get_cooling_data(&pdev->dev, ctx);
- if (ret)
+ if (!ret) {
+ ctx->pwm_fan_state = ctx->pwm_fan_max_state;
+ cdev = thermal_of_cooling_device_register(pdev->dev.of_node,
+ "pwm-fan", ctx,
+ &pwm_fan_cooling_ops);
+ if (IS_ERR(cdev)) {
+ dev_err(&pdev->dev,
+ "Failed to register pwm-fan as cooling device");
+ pwm_disable(ctx->pwm);
+ return PTR_ERR(cdev);
+ }
+ thermal_cdev_update(cdev);
+ } else if (ret != -EINVAL) {
return ret;
+ }
return 0;
}
--
2.0.0.rc2
On Fri, Feb 06, 2015 at 05:59:06PM +0100, Lukasz Majewski wrote:
> It was necessary to decouple code handling writing to sysfs from the one
> responsible for setting PWM of the fan.
> Due to that, new __set_pwm() method was extracted, which is responsible for
> only setting new PWM duty cycle.
>
> Signed-off-by: Lukasz Majewski <[email protected]>
> ---
> Changes for v2:
> - None
> Changes for v3:
> - The commit headline has been reedited.
> ---
> drivers/hwmon/pwm-fan.c | 35 ++++++++++++++++++++++-------------
> 1 file changed, 22 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
> index 1991d903..870e100 100644
> --- a/drivers/hwmon/pwm-fan.c
> +++ b/drivers/hwmon/pwm-fan.c
> @@ -33,21 +33,15 @@ struct pwm_fan_ctx {
> unsigned char pwm_value;
> };
>
> -static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
> - const char *buf, size_t count)
> +static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
> {
> - struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
> - unsigned long pwm, duty;
> - ssize_t ret;
> -
> - if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
> - return -EINVAL;
> -
> - mutex_lock(&ctx->lock);
> + unsigned long duty;
> + int ret;
>
> if (ctx->pwm_value == pwm)
> - goto exit_set_pwm_no_change;
> + return 0;
>
Why did you move this check outside the lock ? With this change there
is no guarantee that pwm_value wasn't changed while waiting for the lock.
Guenter
> + mutex_lock(&ctx->lock);
> if (pwm == 0) {
> pwm_disable(ctx->pwm);
> goto exit_set_pwm;
> @@ -66,13 +60,28 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
>
> exit_set_pwm:
> ctx->pwm_value = pwm;
> -exit_set_pwm_no_change:
> - ret = count;
> exit_set_pwm_err:
> mutex_unlock(&ctx->lock);
> return ret;
> }
>
> +static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
> + unsigned long pwm;
> + int ret;
> +
> + if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
> + return -EINVAL;
> +
> + ret = __set_pwm(ctx, pwm);
> + if (ret)
> + return ret;
> +
> + return count;
> +}
> +
> static ssize_t show_pwm(struct device *dev,
> struct device_attribute *attr, char *buf)
> {
> --
> 2.0.0.rc2
>
On Fri, Feb 06, 2015 at 05:59:07PM +0100, Lukasz Majewski wrote:
> This patch provides code for reading PWM FAN configuration data via
> device tree. The pwm-fan can work with full speed when configuration
> is not provided. However, errors are propagated when wrong DT bindings
> are found.
> Additionally the struct pwm_fan_ctx has been extended.
>
> Signed-off-by: Lukasz Majewski <[email protected]>
> ---
> Changes for v2:
> - Rename pwm_fan_max_states to pwm_fan_cooling_levels
> - Moving pwm_fan_of_get_cooling_data() call after setting end enabling PWM FAN
> - pwm_fan_of_get_cooling_data() now can fail - preserving old behaviour
> - Remove unnecessary dev_err() call
> Changes for v3:
> - Patch's headline has been reedited
> - pwm_fan_of_get_cooling_data() return code is now being checked.
> - of_property_count_elems_of_size() is now used instead of_find_property()
> - More verbose patch description added
> ---
> drivers/hwmon/pwm-fan.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 53 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
> index 870e100..f3f5843 100644
> --- a/drivers/hwmon/pwm-fan.c
> +++ b/drivers/hwmon/pwm-fan.c
> @@ -30,7 +30,10 @@
> struct pwm_fan_ctx {
> struct mutex lock;
> struct pwm_device *pwm;
> - unsigned char pwm_value;
> + unsigned int pwm_value;
> + unsigned int pwm_fan_state;
> + unsigned int pwm_fan_max_state;
> + unsigned int *pwm_fan_cooling_levels;
> };
>
> static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
> @@ -100,6 +103,50 @@ static struct attribute *pwm_fan_attrs[] = {
>
> ATTRIBUTE_GROUPS(pwm_fan);
>
> +int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
> +{
> + struct device_node *np = dev->of_node;
> + int num, i, ret;
> +
> + ret = of_property_count_elems_of_size(np, "cooling-levels",
> + sizeof(u32));
> +
> + if (ret == -EINVAL) {
> + dev_err(dev, "Property 'cooling-levels' not found\n");
> + return 0;
Returning 0 is obviously not an error, otherwise you would not return 0 here.
So dev_err is wrong. Also, the message itself is wrong; the property may
well be there but have a wrong size.
> + }
> +
> + if (ret <= 0) {
> + dev_err(dev, "Wrong data!\n");
> + return ret;
> + }
This will result in the driver failing to load if devicetree is not configured
(of_property_count_elems_of_size will return -ENOSYS). This is not acceptable.
Also, if the call returns 0 you don't return an error but display a "Wrong data!"
error message. Either it is an error or it is not, but not both.
Guenter
On Fri, 6 Feb 2015 10:27:25 -0800
Guenter Roeck <[email protected]> wrote:
> On Fri, Feb 06, 2015 at 05:59:06PM +0100, Lukasz Majewski wrote:
> > It was necessary to decouple code handling writing to sysfs from
> > the one responsible for setting PWM of the fan.
> > Due to that, new __set_pwm() method was extracted, which is
> > responsible for only setting new PWM duty cycle.
> >
> > Signed-off-by: Lukasz Majewski <[email protected]>
> > ---
> > Changes for v2:
> > - None
> > Changes for v3:
> > - The commit headline has been reedited.
> > ---
> > drivers/hwmon/pwm-fan.c | 35 ++++++++++++++++++++++-------------
> > 1 file changed, 22 insertions(+), 13 deletions(-)
> >
> > diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
> > index 1991d903..870e100 100644
> > --- a/drivers/hwmon/pwm-fan.c
> > +++ b/drivers/hwmon/pwm-fan.c
> > @@ -33,21 +33,15 @@ struct pwm_fan_ctx {
> > unsigned char pwm_value;
> > };
> >
> > -static ssize_t set_pwm(struct device *dev, struct device_attribute
> > *attr,
> > - const char *buf, size_t count)
> > +static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
> > {
> > - struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
> > - unsigned long pwm, duty;
> > - ssize_t ret;
> > -
> > - if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
> > - return -EINVAL;
> > -
> > - mutex_lock(&ctx->lock);
> > + unsigned long duty;
> > + int ret;
> >
> > if (ctx->pwm_value == pwm)
> > - goto exit_set_pwm_no_change;
> > + return 0;
> >
> Why did you move this check outside the lock ? With this change there
> is no guarantee that pwm_value wasn't changed while waiting for the
> lock.
Grrr. You are obviously right here. I will fix this. Thanks for spotting
Best regards,
Lukasz Majewski
>
> Guenter
>
> > + mutex_lock(&ctx->lock);
> > if (pwm == 0) {
> > pwm_disable(ctx->pwm);
> > goto exit_set_pwm;
> > @@ -66,13 +60,28 @@ static ssize_t set_pwm(struct device *dev,
> > struct device_attribute *attr,
> > exit_set_pwm:
> > ctx->pwm_value = pwm;
> > -exit_set_pwm_no_change:
> > - ret = count;
> > exit_set_pwm_err:
> > mutex_unlock(&ctx->lock);
> > return ret;
> > }
> >
> > +static ssize_t set_pwm(struct device *dev, struct device_attribute
> > *attr,
> > + const char *buf, size_t count)
> > +{
> > + struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
> > + unsigned long pwm;
> > + int ret;
> > +
> > + if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
> > + return -EINVAL;
> > +
> > + ret = __set_pwm(ctx, pwm);
> > + if (ret)
> > + return ret;
> > +
> > + return count;
> > +}
> > +
> > static ssize_t show_pwm(struct device *dev,
> > struct device_attribute *attr, char *buf)
> > {
> > --
> > 2.0.0.rc2
> >
On Fri, 6 Feb 2015 10:36:57 -0800
Guenter Roeck <[email protected]> wrote:
> On Fri, Feb 06, 2015 at 05:59:07PM +0100, Lukasz Majewski wrote:
> > This patch provides code for reading PWM FAN configuration data via
> > device tree. The pwm-fan can work with full speed when configuration
> > is not provided. However, errors are propagated when wrong DT
> > bindings are found.
> > Additionally the struct pwm_fan_ctx has been extended.
> >
> > Signed-off-by: Lukasz Majewski <[email protected]>
> > ---
> > Changes for v2:
> > - Rename pwm_fan_max_states to pwm_fan_cooling_levels
> > - Moving pwm_fan_of_get_cooling_data() call after setting end
> > enabling PWM FAN
> > - pwm_fan_of_get_cooling_data() now can fail - preserving old
> > behaviour
> > - Remove unnecessary dev_err() call
> > Changes for v3:
> > - Patch's headline has been reedited
> > - pwm_fan_of_get_cooling_data() return code is now being checked.
> > - of_property_count_elems_of_size() is now used instead
> > of_find_property()
> > - More verbose patch description added
> > ---
> > drivers/hwmon/pwm-fan.c | 54
> > ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed,
> > 53 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
> > index 870e100..f3f5843 100644
> > --- a/drivers/hwmon/pwm-fan.c
> > +++ b/drivers/hwmon/pwm-fan.c
> > @@ -30,7 +30,10 @@
> > struct pwm_fan_ctx {
> > struct mutex lock;
> > struct pwm_device *pwm;
> > - unsigned char pwm_value;
> > + unsigned int pwm_value;
> > + unsigned int pwm_fan_state;
> > + unsigned int pwm_fan_max_state;
> > + unsigned int *pwm_fan_cooling_levels;
> > };
> >
> > static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
> > @@ -100,6 +103,50 @@ static struct attribute *pwm_fan_attrs[] = {
> >
> > ATTRIBUTE_GROUPS(pwm_fan);
> >
> > +int pwm_fan_of_get_cooling_data(struct device *dev, struct
> > pwm_fan_ctx *ctx) +{
> > + struct device_node *np = dev->of_node;
> > + int num, i, ret;
> > +
> > + ret = of_property_count_elems_of_size(np, "cooling-levels",
> > + sizeof(u32));
> > +
> > + if (ret == -EINVAL) {
> > + dev_err(dev, "Property 'cooling-levels' not
> > found\n");
> > + return 0;
>
> Returning 0 is obviously not an error, otherwise you would not return
> 0 here. So dev_err is wrong.
pr_info would be more appropriate here.
> Also, the message itself is wrong; the
> property may well be there but have a wrong size.
As fair as I remember the -EINVAL is set only when the property is not
defined. Such situation is correct and our pwm-fan driver should work
with or without it.
>
> > + }
> > +
> > + if (ret <= 0) {
> > + dev_err(dev, "Wrong data!\n");
> > + return ret;
> > + }
>
> This will result in the driver failing to load if devicetree is not
> configured (of_property_count_elems_of_size will return -ENOSYS).
As you pointed out in the comment to v2:
It is OK if the "cooling-levels" is not defined in DT. However, if it
has broken definition, then we should return error. This is what we do
here.
> This is not acceptable. Also, if the call returns 0 you don't return
> an error but display a "Wrong data!" error message. Either it is an
> error or it is not, but not both.
This is an error. "cooling-levels" with zero elements is regarded as a
broken property.
>
> Guenter
Best regards,
Lukasz Majewski
On 02/08/2015 01:36 PM, Lukasz Majewski wrote:
> On Fri, 6 Feb 2015 10:36:57 -0800
> Guenter Roeck <[email protected]> wrote:
>
>> On Fri, Feb 06, 2015 at 05:59:07PM +0100, Lukasz Majewski wrote:
>>> This patch provides code for reading PWM FAN configuration data via
>>> device tree. The pwm-fan can work with full speed when configuration
>>> is not provided. However, errors are propagated when wrong DT
>>> bindings are found.
>>> Additionally the struct pwm_fan_ctx has been extended.
>>>
>>> Signed-off-by: Lukasz Majewski <[email protected]>
>>> ---
>>> Changes for v2:
>>> - Rename pwm_fan_max_states to pwm_fan_cooling_levels
>>> - Moving pwm_fan_of_get_cooling_data() call after setting end
>>> enabling PWM FAN
>>> - pwm_fan_of_get_cooling_data() now can fail - preserving old
>>> behaviour
>>> - Remove unnecessary dev_err() call
>>> Changes for v3:
>>> - Patch's headline has been reedited
>>> - pwm_fan_of_get_cooling_data() return code is now being checked.
>>> - of_property_count_elems_of_size() is now used instead
>>> of_find_property()
>>> - More verbose patch description added
>>> ---
>>> drivers/hwmon/pwm-fan.c | 54
>>> ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed,
>>> 53 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
>>> index 870e100..f3f5843 100644
>>> --- a/drivers/hwmon/pwm-fan.c
>>> +++ b/drivers/hwmon/pwm-fan.c
>>> @@ -30,7 +30,10 @@
>>> struct pwm_fan_ctx {
>>> struct mutex lock;
>>> struct pwm_device *pwm;
>>> - unsigned char pwm_value;
>>> + unsigned int pwm_value;
>>> + unsigned int pwm_fan_state;
>>> + unsigned int pwm_fan_max_state;
>>> + unsigned int *pwm_fan_cooling_levels;
>>> };
>>>
>>> static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
>>> @@ -100,6 +103,50 @@ static struct attribute *pwm_fan_attrs[] = {
>>>
>>> ATTRIBUTE_GROUPS(pwm_fan);
>>>
>>> +int pwm_fan_of_get_cooling_data(struct device *dev, struct
>>> pwm_fan_ctx *ctx) +{
>>> + struct device_node *np = dev->of_node;
>>> + int num, i, ret;
>>> +
>>> + ret = of_property_count_elems_of_size(np, "cooling-levels",
>>> + sizeof(u32));
>>> +
>>> + if (ret == -EINVAL) {
>>> + dev_err(dev, "Property 'cooling-levels' not
>>> found\n");
>>> + return 0;
>>
>> Returning 0 is obviously not an error, otherwise you would not return
>> 0 here. So dev_err is wrong.
>
> pr_info would be more appropriate here.
>
>> Also, the message itself is wrong; the
>> property may well be there but have a wrong size.
>
> As fair as I remember the -EINVAL is set only when the property is not
> defined. Such situation is correct and our pwm-fan driver should work
> with or without it.
>
of_property_count_elems_of_size returns -EINVAL if np is NULL or if
there is no peoperty or prop->length % elem_size != 0, and -ENODATA
if there is no value associated with the property.
If -EINVAL is not an error, please no message. Noisy drivers are
just that, noisy.
>>
>>> + }
>>> +
>>> + if (ret <= 0) {
>>> + dev_err(dev, "Wrong data!\n");
>>> + return ret;
>>> + }
>>
>> This will result in the driver failing to load if devicetree is not
>> configured (of_property_count_elems_of_size will return -ENOSYS).
>
> As you pointed out in the comment to v2:
>
> It is OK if the "cooling-levels" is not defined in DT. However, if it
> has broken definition, then we should return error. This is what we do
> here.
>
You don't return an error, you return 0, at least in some circumstances.
>> This is not acceptable. Also, if the call returns 0 you don't return
>> an error but display a "Wrong data!" error message. Either it is an
>> error or it is not, but not both.
>
> This is an error. "cooling-levels" with zero elements is regarded as a
> broken property.
>
It returns -ENOSYS if DT is not configured, which is still unacceptable.
And, again, if ret == 0 is an error, you should return an error code,
not display an error message and return 0.
Guenter
Presented patches add support for Odroid's U3 optional CPU FAN, which uses PWM
subsystem for low level control.
After successful probe it registers itself as a cooling device for thermal
subsystem. To preserve the ability to use this fan as a PWM device stubs for
thermal_of_cooling_device_register() and thermal_cdev_update() have been added.
Additionally, devices without support for DTS are still supported.
To provide correct functionality, new properties to device tree description for
Exynos4412 and in particular Odroid U3 have been added.
Those patches were tested on Exynos4412 - Odroid U3 board.
Patches were applied on:
linux-soc-thermal/next branch (Linux 3.19-rc5)
SHA1: 252454f5cbda2c6b40c5d36f58cac2938437b85d
Kamil Debski (1):
ARM: dts: Add pwm-fan node to the Odroid-U3 board
Lukasz Majewski (7):
thermal: Provide stub for thermal_of_cooling_device_register()
function
thermal: Provide stub for thermal_cdev_update() function
Documentation: dts: Documentation entry to explain how to use PWM FAN
as a cooling device
ARM: dts: Add properties to use pwm-fan device as a cooling device in
Odroid U3
hwmon: pwm-fan: Extract __set_pwm() function to only modify PWM duty
cycle
hwmon: pwm-fan: Read PWM FAN configuration from device tree
hwmon: pwm-fan: Code for using PWM FAN as a cooling device
.../devicetree/bindings/hwmon/pwm-fan.txt | 20 +++
arch/arm/boot/dts/exynos4.dtsi | 2 +-
arch/arm/boot/dts/exynos4412-odroidu3.dts | 44 ++++++
drivers/hwmon/pwm-fan.c | 167 +++++++++++++++++++--
include/linux/thermal.h | 22 ++-
5 files changed, 236 insertions(+), 19 deletions(-)
--
2.0.0.rc2
Odroid U3 fan can work without being registered as OF cooling device
(with CONFIG_THERMAL_OF disabled).
In this situation it can be controlled via PWM entry at
/sys/class/hwmon/hwmon0/pwm1.
Therefore, the thermal_of_cooling_device_register() function needs a stub
to allow clean compilation.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- None
Changes for v3:
- Provide stub declaration when CONFIG_THERMAL is not set
Changes for v4:
- None
---
include/linux/thermal.h | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index fc52e30..eacf2de 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -293,6 +293,20 @@ struct thermal_trip {
};
/* Function declarations */
+#ifdef CONFIG_THERMAL
+struct thermal_cooling_device *
+thermal_of_cooling_device_register(struct device_node *np,
+ char *type, void *devdata,
+ const struct thermal_cooling_device_ops *);
+#else
+static inline struct thermal_cooling_device *
+thermal_of_cooling_device_register(struct device_node *np,
+ char *type, void *devdata,
+ const struct thermal_cooling_device_ops *ops)
+{
+ return NULL;
+}
+#endif
#ifdef CONFIG_THERMAL_OF
struct thermal_zone_device *
thermal_zone_of_sensor_register(struct device *dev, int id, void *data,
@@ -328,9 +342,6 @@ void thermal_zone_device_update(struct thermal_zone_device *);
struct thermal_cooling_device *thermal_cooling_device_register(char *, void *,
const struct thermal_cooling_device_ops *);
-struct thermal_cooling_device *
-thermal_of_cooling_device_register(struct device_node *np, char *, void *,
- const struct thermal_cooling_device_ops *);
void thermal_cooling_device_unregister(struct thermal_cooling_device *);
struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name);
int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp);
--
2.0.0.rc2
Odroid U3 fan can work without being registered as OF cooling device
(with CONFIG_THERMAL{_OF|} disabled).
In this situation it can be controlled via PWM entry at
/sys/class/hwmon/hwmon0/pwm1.
Therefore, the thermal_cdev_update() function needs a stub
to allow clean compilation.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- New patch
Changes for v3:
- thermal_cdev_update() now depends on CONFIG_THERMAL flag
Changes for v4:
- None
---
include/linux/thermal.h | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index eacf2de..25382e6 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -298,6 +298,7 @@ struct thermal_cooling_device *
thermal_of_cooling_device_register(struct device_node *np,
char *type, void *devdata,
const struct thermal_cooling_device_ops *);
+void thermal_cdev_update(struct thermal_cooling_device *);
#else
static inline struct thermal_cooling_device *
thermal_of_cooling_device_register(struct device_node *np,
@@ -306,6 +307,9 @@ thermal_of_cooling_device_register(struct device_node *np,
{
return NULL;
}
+static inline void thermal_cdev_update(struct thermal_cooling_device *cdev)
+{
+}
#endif
#ifdef CONFIG_THERMAL_OF
struct thermal_zone_device *
@@ -349,7 +353,6 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp);
int get_tz_trend(struct thermal_zone_device *, int);
struct thermal_instance *get_thermal_instance(struct thermal_zone_device *,
struct thermal_cooling_device *, int);
-void thermal_cdev_update(struct thermal_cooling_device *);
void thermal_notify_framework(struct thermal_zone_device *, int);
#ifdef CONFIG_NET
--
2.0.0.rc2
Explanation of several properties, which allow PWM fan working as a cooling
device, have been embraced in this commit.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- Rename cooling-pwm-values to cooling-levels
- Remove default-pulse-width property and stick to default hwmon policy
Changes for v3:
- Changing commit title from "hwmon: dts: Doc:" to "Documentation: dts"
- Remove unnecessary properties
- Set maximal cooling level to 230 instead of 255
Changes for v4:
- None
---
Documentation/devicetree/bindings/hwmon/pwm-fan.txt | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
index 610757c..d53fe0c 100644
--- a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
+++ b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
@@ -3,10 +3,30 @@ Bindings for a fan connected to the PWM lines
Required properties:
- compatible : "pwm-fan"
- pwms : the PWM that is used to control the PWM fan
+- cooling-levels : PWM duty cycle values in a range from 0 to 255
+ which correspond to thermal cooling states
+
+Thorough description of the following bindings:
+ cooling-min-state = <0>;
+ cooling-max-state = <3>;
+ #cooling-cells = <2>;
+ thermal-zone {
+ cpu_thermal: cpu-thermal {
+ cooling-maps {
+ map0 {
+ trip = <&cpu_alert1>;
+ cooling-device = <&fan0 0 1>;
+ };
+ };
+ };
+
+for PWM FAN used as cooling device can be found at:
+./Documentation/devicetree/bindings/thermal/thermal.txt
Example:
pwm-fan {
compatible = "pwm-fan";
status = "okay";
pwms = <&pwm 0 10000 0>;
+ cooling-levels = <0 102 170 230>;
};
--
2.0.0.rc2
From: Kamil Debski <[email protected]>
Add pwm-fan node to the Odroid-U3 board file to enable PWM control of the
cooling fan. In addition, add the "pwm" label to the pwm@139D0000 node
in the exynos4412.dtsi.
Signed-off-by: Kamil Debski <[email protected]>
[Rebased on the newest mainline by [email protected]]
---
Changes since v1:
- added pwm label to the pwm@139D0000 node in exynos4.dtsi
- use the pwm label in the exynos4412-odroidu3.dts
- change order or properties in the pwn-fan node, to be sorted
in alphabetical order
---
arch/arm/boot/dts/exynos4.dtsi | 2 +-
arch/arm/boot/dts/exynos4412-odroidu3.dts | 13 +++++++++++++
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi
index f18d746..75266e3 100644
--- a/arch/arm/boot/dts/exynos4.dtsi
+++ b/arch/arm/boot/dts/exynos4.dtsi
@@ -582,7 +582,7 @@
status = "disabled";
};
- pwm@139D0000 {
+ pwm: pwm@139D0000 {
compatible = "samsung,exynos4210-pwm";
reg = <0x139D0000 0x1000>;
interrupts = <0 37 0>, <0 38 0>, <0 39 0>, <0 40 0>, <0 41 0>;
diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts
index c8a64be..60bd1e4 100644
--- a/arch/arm/boot/dts/exynos4412-odroidu3.dts
+++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts
@@ -31,6 +31,19 @@
linux,default-trigger = "heartbeat";
};
};
+
+ pwm-fan {
+ compatible = "pwm-fan";
+ pwms = <&pwm 0 10000 0>;
+ status = "okay";
+ };
+};
+
+&pwm {
+ pinctrl-0 = <&pwm0_out>;
+ pinctrl-names = "default";
+ samsung,pwm-outputs = <0>;
+ status = "okay";
};
&usb3503 {
--
2.0.0.rc2
With those bindings it is possible to use pwm-fan device available in
Odroid U3 as a cooling device.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- Rename cooling-pwm-values property to cooling-levels
Changes for v3:
- Change patch's topic to "ARM dts"
- Reduce maximal cooling-level to 230 from 255
Changes for v4:
- None
---
arch/arm/boot/dts/exynos4412-odroidu3.dts | 33 ++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts
index 60bd1e4..3e5df3e 100644
--- a/arch/arm/boot/dts/exynos4412-odroidu3.dts
+++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts
@@ -32,11 +32,42 @@
};
};
- pwm-fan {
+ fan0: pwm-fan {
compatible = "pwm-fan";
pwms = <&pwm 0 10000 0>;
+ cooling-min-state = <0>;
+ cooling-max-state = <3>;
+ #cooling-cells = <2>;
+ cooling-levels = <0 102 170 230>;
status = "okay";
};
+
+ thermal-zones {
+ cpu_thermal: cpu-thermal {
+ cooling-maps {
+ map0 {
+ trip = <&cpu_alert1>;
+ cooling-device = <&cpu0 7 7>;
+ };
+ map1 {
+ trip = <&cpu_alert2>;
+ cooling-device = <&cpu0 13 13>;
+ };
+ map2 {
+ trip = <&cpu_alert0>;
+ cooling-device = <&fan0 0 1>;
+ };
+ map3 {
+ trip = <&cpu_alert1>;
+ cooling-device = <&fan0 1 2>;
+ };
+ map4 {
+ trip = <&cpu_alert2>;
+ cooling-device = <&fan0 2 3>;
+ };
+ };
+ };
+ };
};
&pwm {
--
2.0.0.rc2
It was necessary to decouple code handling writing to sysfs from the one
responsible for setting PWM of the fan.
Due to that, new __set_pwm() method was extracted, which is responsible for
only setting new PWM duty cycle.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- None
Changes for v3:
- The commit headline has been reedited.
Changes for v4:
- Protect "if (ctx->pwm_value == pwm)" with ctx lock
- Initialize ret with zero
---
drivers/hwmon/pwm-fan.c | 33 +++++++++++++++++++++------------
1 file changed, 21 insertions(+), 12 deletions(-)
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 1991d903..bd42d39 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -33,20 +33,14 @@ struct pwm_fan_ctx {
unsigned char pwm_value;
};
-static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
{
- struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
- unsigned long pwm, duty;
- ssize_t ret;
-
- if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
- return -EINVAL;
+ unsigned long duty;
+ int ret = 0;
mutex_lock(&ctx->lock);
-
if (ctx->pwm_value == pwm)
- goto exit_set_pwm_no_change;
+ goto exit_set_pwm_err;
if (pwm == 0) {
pwm_disable(ctx->pwm);
@@ -66,13 +60,28 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
exit_set_pwm:
ctx->pwm_value = pwm;
-exit_set_pwm_no_change:
- ret = count;
exit_set_pwm_err:
mutex_unlock(&ctx->lock);
return ret;
}
+static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
+ unsigned long pwm;
+ int ret;
+
+ if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
+ return -EINVAL;
+
+ ret = __set_pwm(ctx, pwm);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
static ssize_t show_pwm(struct device *dev,
struct device_attribute *attr, char *buf)
{
--
2.0.0.rc2
This patch provides code for reading PWM FAN configuration data via
device tree. The pwm-fan can work with full speed when configuration
is not provided. However, errors are propagated when wrong DT bindings
are found.
Additionally the struct pwm_fan_ctx has been extended.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- Rename pwm_fan_max_states to pwm_fan_cooling_levels
- Moving pwm_fan_of_get_cooling_data() call after setting end enabling PWM FAN
- pwm_fan_of_get_cooling_data() now can fail - preserving old behaviour
- Remove unnecessary dev_err() call
Changes for v3:
- Patch's headline has been reedited
- pwm_fan_of_get_cooling_data() return code is now being checked.
- of_property_count_elems_of_size() is now used instead of_find_property()
- More verbose patch description added
Changes for v4:
- dev_err() has been removed from pwm_fan_of_get_cooling_data()
- Returning -EINVAL when "cooling-levels" are defined in DT, but doesn't
have the value
---
drivers/hwmon/pwm-fan.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 51 insertions(+), 1 deletion(-)
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index bd42d39..82cd06a 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -30,7 +30,10 @@
struct pwm_fan_ctx {
struct mutex lock;
struct pwm_device *pwm;
- unsigned char pwm_value;
+ unsigned int pwm_value;
+ unsigned int pwm_fan_state;
+ unsigned int pwm_fan_max_state;
+ unsigned int *pwm_fan_cooling_levels;
};
static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
@@ -100,6 +103,48 @@ static struct attribute *pwm_fan_attrs[] = {
ATTRIBUTE_GROUPS(pwm_fan);
+int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
+{
+ struct device_node *np = dev->of_node;
+ int num, i, ret;
+
+ ret = of_property_count_elems_of_size(np, "cooling-levels",
+ sizeof(u32));
+
+ if (ret == -EINVAL)
+ return 0;
+
+ if (ret <= 0) {
+ dev_err(dev, "Wrong data!\n");
+ return ret ? ret : -EINVAL;
+ }
+
+ num = ret;
+ ctx->pwm_fan_cooling_levels = devm_kzalloc(dev, num * sizeof(u32),
+ GFP_KERNEL);
+ if (!ctx->pwm_fan_cooling_levels)
+ return -ENOMEM;
+
+ ret = of_property_read_u32_array(np, "cooling-levels",
+ ctx->pwm_fan_cooling_levels, num);
+ if (ret) {
+ dev_err(dev, "Property 'cooling-levels' cannot be read!\n");
+ return ret;
+ }
+
+ for (i = 0; i < num; i++) {
+ if (ctx->pwm_fan_cooling_levels[i] > MAX_PWM) {
+ dev_err(dev, "PWM fan state[%d]:%d > %d\n", i,
+ ctx->pwm_fan_cooling_levels[i], MAX_PWM);
+ return -EINVAL;
+ }
+ }
+
+ ctx->pwm_fan_max_state = num - 1;
+
+ return 0;
+}
+
static int pwm_fan_probe(struct platform_device *pdev)
{
struct device *hwmon;
@@ -145,6 +190,11 @@ static int pwm_fan_probe(struct platform_device *pdev)
pwm_disable(ctx->pwm);
return PTR_ERR(hwmon);
}
+
+ ret = pwm_fan_of_get_cooling_data(&pdev->dev, ctx);
+ if (ret)
+ return ret;
+
return 0;
}
--
2.0.0.rc2
The PWM FAN device can now be used as a thermal cooling device. Necessary
infrastructure has been added in this commit.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- Replace pwm_fan_cooling_states with pwm_fan_cooling_levels
- Update ctx->pwm_fan_state when correct data from device tree is available
- Using therma_cdev_update() when thermal is ready for controlling the fan
Changes for v3:
- Rename patch heading
- pwm_fan_of_get_cooling_data() now returns -EINVAL when no "cooling-levels"
property defined
- register of cooling device only when proper cooling data is present
Changed for v4:
- None
---
drivers/hwmon/pwm-fan.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 83 insertions(+), 3 deletions(-)
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 82cd06a..622bcd1 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -24,6 +24,7 @@
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/sysfs.h>
+#include <linux/thermal.h>
#define MAX_PWM 255
@@ -68,6 +69,17 @@ exit_set_pwm_err:
return ret;
}
+static void pwm_fan_update_state(struct pwm_fan_ctx *ctx, unsigned long pwm)
+{
+ int i;
+
+ for (i = 0; i < ctx->pwm_fan_max_state; ++i)
+ if (pwm < ctx->pwm_fan_cooling_levels[i + 1])
+ break;
+
+ ctx->pwm_fan_state = i;
+}
+
static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -82,6 +94,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
if (ret)
return ret;
+ pwm_fan_update_state(ctx, pwm);
return count;
}
@@ -103,6 +116,59 @@ static struct attribute *pwm_fan_attrs[] = {
ATTRIBUTE_GROUPS(pwm_fan);
+/* thermal cooling device callbacks */
+static int pwm_fan_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+
+ *state = ctx->pwm_fan_max_state;
+
+ return 0;
+}
+
+static int pwm_fan_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+
+ if (!ctx)
+ return -EINVAL;
+
+ *state = ctx->pwm_fan_state;
+
+ return 0;
+}
+
+static int
+pwm_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+ int ret;
+
+ if (!ctx || (state > ctx->pwm_fan_max_state))
+ return -EINVAL;
+
+ if (state == ctx->pwm_fan_state)
+ return 0;
+
+ ret = __set_pwm(ctx, ctx->pwm_fan_cooling_levels[state]);
+ if (ret) {
+ dev_err(&cdev->device, "Cannot set pwm!\n");
+ return ret;
+ }
+
+ ctx->pwm_fan_state = state;
+
+ return ret;
+}
+
+static const struct thermal_cooling_device_ops pwm_fan_cooling_ops = {
+ .get_max_state = pwm_fan_get_max_state,
+ .get_cur_state = pwm_fan_get_cur_state,
+ .set_cur_state = pwm_fan_set_cur_state,
+};
+
int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
{
struct device_node *np = dev->of_node;
@@ -112,7 +178,7 @@ int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
sizeof(u32));
if (ret == -EINVAL)
- return 0;
+ return ret;
if (ret <= 0) {
dev_err(dev, "Wrong data!\n");
@@ -147,8 +213,9 @@ int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
static int pwm_fan_probe(struct platform_device *pdev)
{
- struct device *hwmon;
+ struct thermal_cooling_device *cdev;
struct pwm_fan_ctx *ctx;
+ struct device *hwmon;
int duty_cycle;
int ret;
@@ -192,8 +259,21 @@ static int pwm_fan_probe(struct platform_device *pdev)
}
ret = pwm_fan_of_get_cooling_data(&pdev->dev, ctx);
- if (ret)
+ if (!ret) {
+ ctx->pwm_fan_state = ctx->pwm_fan_max_state;
+ cdev = thermal_of_cooling_device_register(pdev->dev.of_node,
+ "pwm-fan", ctx,
+ &pwm_fan_cooling_ops);
+ if (IS_ERR(cdev)) {
+ dev_err(&pdev->dev,
+ "Failed to register pwm-fan as cooling device");
+ pwm_disable(ctx->pwm);
+ return PTR_ERR(cdev);
+ }
+ thermal_cdev_update(cdev);
+ } else if (ret != -EINVAL) {
return ret;
+ }
return 0;
}
--
2.0.0.rc2
Am 18.02.2015 um 11:07 schrieb Lukasz Majewski:
> From: Kamil Debski <[email protected]>
>
> Add pwm-fan node to the Odroid-U3 board file to enable PWM control of the
> cooling fan. In addition, add the "pwm" label to the pwm@139D0000 node
> in the exynos4412.dtsi.
>
> Signed-off-by: Kamil Debski <[email protected]>
> [Rebased on the newest mainline by [email protected]]
You need to sign off patches you submit.
> ---
> Changes since v1:
> - added pwm label to the pwm@139D0000 node in exynos4.dtsi
> - use the pwm label in the exynos4412-odroidu3.dts
> - change order or properties in the pwn-fan node, to be sorted
> in alphabetical order
>
> ---
> arch/arm/boot/dts/exynos4.dtsi | 2 +-
> arch/arm/boot/dts/exynos4412-odroidu3.dts | 13 +++++++++++++
> 2 files changed, 14 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi
> index f18d746..75266e3 100644
> --- a/arch/arm/boot/dts/exynos4.dtsi
> +++ b/arch/arm/boot/dts/exynos4.dtsi
> @@ -582,7 +582,7 @@
> status = "disabled";
> };
>
> - pwm@139D0000 {
> + pwm: pwm@139D0000 {
> compatible = "samsung,exynos4210-pwm";
> reg = <0x139D0000 0x1000>;
> interrupts = <0 37 0>, <0 38 0>, <0 39 0>, <0 40 0>, <0 41 0>;
> diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts
> index c8a64be..60bd1e4 100644
> --- a/arch/arm/boot/dts/exynos4412-odroidu3.dts
> +++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts
> @@ -31,6 +31,19 @@
> linux,default-trigger = "heartbeat";
> };
> };
> +
> + pwm-fan {
> + compatible = "pwm-fan";
> + pwms = <&pwm 0 10000 0>;
> + status = "okay";
Status "okay" is only needed for pre-existing nodes that would have
status "disabled" otherwise, such as below. Just drop it here.
Regards,
Andreas
> + };
> +};
> +
> +&pwm {
> + pinctrl-0 = <&pwm0_out>;
> + pinctrl-names = "default";
> + samsung,pwm-outputs = <0>;
> + status = "okay";
> };
>
> &usb3503 {
>
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 N?rnberg, Germany
GF: Felix Imend?rffer, Jane Smithard, Jennifer Guild, Dilip Upmanyu,
Graham Norton; HRB 21284 (AG N?rnberg)
On 02/18/2015 02:07 AM, Lukasz Majewski wrote:
> This patch provides code for reading PWM FAN configuration data via
> device tree. The pwm-fan can work with full speed when configuration
> is not provided. However, errors are propagated when wrong DT bindings
> are found.
> Additionally the struct pwm_fan_ctx has been extended.
>
> Signed-off-by: Lukasz Majewski <[email protected]>
> ---
> Changes for v2:
> - Rename pwm_fan_max_states to pwm_fan_cooling_levels
> - Moving pwm_fan_of_get_cooling_data() call after setting end enabling PWM FAN
> - pwm_fan_of_get_cooling_data() now can fail - preserving old behaviour
> - Remove unnecessary dev_err() call
> Changes for v3:
> - Patch's headline has been reedited
> - pwm_fan_of_get_cooling_data() return code is now being checked.
> - of_property_count_elems_of_size() is now used instead of_find_property()
> - More verbose patch description added
> Changes for v4:
> - dev_err() has been removed from pwm_fan_of_get_cooling_data()
> - Returning -EINVAL when "cooling-levels" are defined in DT, but doesn't
> have the value
> ---
> drivers/hwmon/pwm-fan.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 51 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
> index bd42d39..82cd06a 100644
> --- a/drivers/hwmon/pwm-fan.c
> +++ b/drivers/hwmon/pwm-fan.c
> @@ -30,7 +30,10 @@
> struct pwm_fan_ctx {
> struct mutex lock;
> struct pwm_device *pwm;
> - unsigned char pwm_value;
> + unsigned int pwm_value;
> + unsigned int pwm_fan_state;
> + unsigned int pwm_fan_max_state;
> + unsigned int *pwm_fan_cooling_levels;
> };
>
> static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
> @@ -100,6 +103,48 @@ static struct attribute *pwm_fan_attrs[] = {
>
> ATTRIBUTE_GROUPS(pwm_fan);
>
> +int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
> +{
> + struct device_node *np = dev->of_node;
> + int num, i, ret;
> +
> + ret = of_property_count_elems_of_size(np, "cooling-levels",
> + sizeof(u32));
> +
> + if (ret == -EINVAL)
> + return 0;
The function returns -EINVAL if there is no such property,
but also if prop->length % elem_size != 0. The latter _would_
be an error.
Overall I don't entirely understand why you do not call
of_find_property first. If that returns NULL, you would know for sure
that the property does not exist, and you would not have to second
guess the returned error from of_property_count_elems_of_size.
On a side note, there is of_property_count_u32_elems() to count
properties of size u32.
> +
> + if (ret <= 0) {
> + dev_err(dev, "Wrong data!\n");
> + return ret ? ret : -EINVAL;
> + }
If devicetree is not configured, of_property_count_elems_of_size
returns -ENOSYS, which is returned, causing the driver to fail loading.
> +
> + num = ret;
> + ctx->pwm_fan_cooling_levels = devm_kzalloc(dev, num * sizeof(u32),
> + GFP_KERNEL);
> + if (!ctx->pwm_fan_cooling_levels)
> + return -ENOMEM;
> +
> + ret = of_property_read_u32_array(np, "cooling-levels",
> + ctx->pwm_fan_cooling_levels, num);
> + if (ret) {
> + dev_err(dev, "Property 'cooling-levels' cannot be read!\n");
> + return ret;
> + }
> +
> + for (i = 0; i < num; i++) {
> + if (ctx->pwm_fan_cooling_levels[i] > MAX_PWM) {
> + dev_err(dev, "PWM fan state[%d]:%d > %d\n", i,
> + ctx->pwm_fan_cooling_levels[i], MAX_PWM);
> + return -EINVAL;
> + }
> + }
> +
> + ctx->pwm_fan_max_state = num - 1;
> +
> + return 0;
> +}
> +
> static int pwm_fan_probe(struct platform_device *pdev)
> {
> struct device *hwmon;
> @@ -145,6 +190,11 @@ static int pwm_fan_probe(struct platform_device *pdev)
> pwm_disable(ctx->pwm);
> return PTR_ERR(hwmon);
> }
> +
> + ret = pwm_fan_of_get_cooling_data(&pdev->dev, ctx);
> + if (ret)
> + return ret;
> +
> return 0;
> }
>
>
Hi Guenter,
> On 02/18/2015 02:07 AM, Lukasz Majewski wrote:
> > This patch provides code for reading PWM FAN configuration data via
> > device tree. The pwm-fan can work with full speed when configuration
> > is not provided. However, errors are propagated when wrong DT
> > bindings are found.
> > Additionally the struct pwm_fan_ctx has been extended.
> >
> > Signed-off-by: Lukasz Majewski <[email protected]>
> > ---
> > Changes for v2:
> > - Rename pwm_fan_max_states to pwm_fan_cooling_levels
> > - Moving pwm_fan_of_get_cooling_data() call after setting end
> > enabling PWM FAN
> > - pwm_fan_of_get_cooling_data() now can fail - preserving old
> > behaviour
> > - Remove unnecessary dev_err() call
> > Changes for v3:
> > - Patch's headline has been reedited
> > - pwm_fan_of_get_cooling_data() return code is now being checked.
> > - of_property_count_elems_of_size() is now used instead
> > of_find_property()
> > - More verbose patch description added
> > Changes for v4:
> > - dev_err() has been removed from pwm_fan_of_get_cooling_data()
> > - Returning -EINVAL when "cooling-levels" are defined in DT, but
> > doesn't have the value
> > ---
> > drivers/hwmon/pwm-fan.c | 52
> > ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed,
> > 51 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
> > index bd42d39..82cd06a 100644
> > --- a/drivers/hwmon/pwm-fan.c
> > +++ b/drivers/hwmon/pwm-fan.c
> > @@ -30,7 +30,10 @@
> > struct pwm_fan_ctx {
> > struct mutex lock;
> > struct pwm_device *pwm;
> > - unsigned char pwm_value;
> > + unsigned int pwm_value;
> > + unsigned int pwm_fan_state;
> > + unsigned int pwm_fan_max_state;
> > + unsigned int *pwm_fan_cooling_levels;
> > };
> >
> > static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
> > @@ -100,6 +103,48 @@ static struct attribute *pwm_fan_attrs[] = {
> >
> > ATTRIBUTE_GROUPS(pwm_fan);
> >
> > +int pwm_fan_of_get_cooling_data(struct device *dev, struct
> > pwm_fan_ctx *ctx) +{
> > + struct device_node *np = dev->of_node;
> > + int num, i, ret;
> > +
> > + ret = of_property_count_elems_of_size(np, "cooling-levels",
> > + sizeof(u32));
> > +
> > + if (ret == -EINVAL)
> > + return 0;
>
> The function returns -EINVAL if there is no such property,
> but also if prop->length % elem_size != 0. The latter _would_
> be an error.
>
> Overall I don't entirely understand why you do not call
> of_find_property first. If that returns NULL, you would know for sure
> that the property does not exist, and you would not have to second
> guess the returned error from of_property_count_elems_of_size.
For sake of readability I will at v5 first check of_find_property() and
if it is correct, then I will call of_property_count_u32_elems().
>
> On a side note, there is of_property_count_u32_elems() to count
> properties of size u32.
>
> > +
> > + if (ret <= 0) {
> > + dev_err(dev, "Wrong data!\n");
> > + return ret ? ret : -EINVAL;
> > + }
>
> If devicetree is not configured, of_property_count_elems_of_size
> returns -ENOSYS, which is returned, causing the driver to fail
> loading.
Has of_property_count_elems_of_size() returns -ENOSYS?
Maybe something has changed, but in my linux-vanila (3.19-rc4)
at ./drivers/of/base.c it returns -EINVAL, -ENODATA or number of
elements.
Have I missed something?
>
> > +
> > + num = ret;
> > + ctx->pwm_fan_cooling_levels = devm_kzalloc(dev, num *
> > sizeof(u32),
> > + GFP_KERNEL);
> > + if (!ctx->pwm_fan_cooling_levels)
> > + return -ENOMEM;
> > +
> > + ret = of_property_read_u32_array(np, "cooling-levels",
> > +
> > ctx->pwm_fan_cooling_levels, num);
> > + if (ret) {
> > + dev_err(dev, "Property 'cooling-levels' cannot be
> > read!\n");
> > + return ret;
> > + }
> > +
> > + for (i = 0; i < num; i++) {
> > + if (ctx->pwm_fan_cooling_levels[i] > MAX_PWM) {
> > + dev_err(dev, "PWM fan state[%d]:%d >
> > %d\n", i,
> > + ctx->pwm_fan_cooling_levels[i],
> > MAX_PWM);
> > + return -EINVAL;
> > + }
> > + }
> > +
> > + ctx->pwm_fan_max_state = num - 1;
> > +
> > + return 0;
> > +}
> > +
> > static int pwm_fan_probe(struct platform_device *pdev)
> > {
> > struct device *hwmon;
> > @@ -145,6 +190,11 @@ static int pwm_fan_probe(struct
> > platform_device *pdev) pwm_disable(ctx->pwm);
> > return PTR_ERR(hwmon);
> > }
> > +
> > + ret = pwm_fan_of_get_cooling_data(&pdev->dev, ctx);
> > + if (ret)
> > + return ret;
I think that here is the confusing part. Please compare this patch with
the following one.
Here we configure ctx struct via DT. If of_property_count_u32_elems()
returns -EINVAL, then we consider that "cooling-levels" wasn't defined
in DT and return with 0. Other error codes are considered as errors
and probe return error code.
> > +
> > return 0;
> > }
> >
> >
>
--
Best regards,
Lukasz Majewski
Samsung R&D Institute Poland (SRPOL) | Linux Platform Group
On Mon, Feb 23, 2015 at 05:13:36PM +0100, Lukasz Majewski wrote:
> Hi Guenter,
>
[ ... ]
> >
> > If devicetree is not configured, of_property_count_elems_of_size
> > returns -ENOSYS, which is returned, causing the driver to fail
> > loading.
>
> Has of_property_count_elems_of_size() returns -ENOSYS?
>
> Maybe something has changed, but in my linux-vanila (3.19-rc4)
> at ./drivers/of/base.c it returns -EINVAL, -ENODATA or number of
> elements.
>
> Have I missed something?
>
Hi Lukasz,
Yes, you have. Check include/linux/of.h, line 484, in latest mainline.
Guenter
Hi Guenter,
> On Mon, Feb 23, 2015 at 05:13:36PM +0100, Lukasz Majewski wrote:
> > Hi Guenter,
> >
> [ ... ]
>
> > >
> > > If devicetree is not configured, of_property_count_elems_of_size
> > > returns -ENOSYS, which is returned, causing the driver to fail
> > > loading.
> >
> > Has of_property_count_elems_of_size() returns -ENOSYS?
> >
> > Maybe something has changed, but in my linux-vanila (3.19-rc4)
> > at ./drivers/of/base.c it returns -EINVAL, -ENODATA or number of
> > elements.
> >
> > Have I missed something?
> >
> Hi Lukasz,
>
> Yes, you have. Check include/linux/of.h, line 484, in latest mainline.
Ok. Now I got it.
The above situation shouldn't happen if I put of_find_property() check
on the very beginning of this function (it returns NULL when DT support
is not compiled).
The function would look as follows:
int
pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx
*ctx)
{
struct device_node *np = dev->of_node;
int num, i, ret;
if (!of_find_property(np, "cooling-levels", NULL))
return 0;
ret = of_property_count_u32_elems(np, "cooling-levels");
if (ret <= 0) {
dev_err(dev, "Wrong data!\n");
return ret;
}
num = ret;
ctx->pwm_fan_cooling_levels = devm_kzalloc(dev, num *
sizeof(u32), GFP_KERNEL);
if (!ctx->pwm_fan_cooling_levels)
return -ENOMEM;
ret = of_property_read_u32_array(np, "cooling-levels",
ctx->pwm_fan_cooling_levels,
num);
if (ret) {
dev_err(dev, "Property 'cooling-levels' cannot be
read!\n");
return ret;
}
for (i = 0; i < num; i++) {
if (ctx->pwm_fan_cooling_levels[i] > MAX_PWM) {
dev_err(dev, "PWM fan state[%d]:%d > %d\n", i,
ctx->pwm_fan_cooling_levels[i],
MAX_PWM);
return -EINVAL;
}
}
ctx->pwm_fan_max_state = num - 1;
return 0;
}
>
> Guenter
--
Best regards,
Lukasz Majewski
Samsung R&D Institute Poland (SRPOL) | Linux Platform Group
On Mon, Feb 23, 2015 at 05:51:22PM +0100, Lukasz Majewski wrote:
> Hi Guenter,
>
> > On Mon, Feb 23, 2015 at 05:13:36PM +0100, Lukasz Majewski wrote:
> > > Hi Guenter,
> > >
> > [ ... ]
> >
> > > >
> > > > If devicetree is not configured, of_property_count_elems_of_size
> > > > returns -ENOSYS, which is returned, causing the driver to fail
> > > > loading.
> > >
> > > Has of_property_count_elems_of_size() returns -ENOSYS?
> > >
> > > Maybe something has changed, but in my linux-vanila (3.19-rc4)
> > > at ./drivers/of/base.c it returns -EINVAL, -ENODATA or number of
> > > elements.
> > >
> > > Have I missed something?
> > >
> > Hi Lukasz,
> >
> > Yes, you have. Check include/linux/of.h, line 484, in latest mainline.
>
> Ok. Now I got it.
>
> The above situation shouldn't happen if I put of_find_property() check
> on the very beginning of this function (it returns NULL when DT support
> is not compiled).
>
Correct.
> The function would look as follows:
>
> int
> pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx
> *ctx)
> {
> struct device_node *np = dev->of_node;
> int num, i, ret;
>
> if (!of_find_property(np, "cooling-levels", NULL))
> return 0;
>
> ret = of_property_count_u32_elems(np, "cooling-levels");
> if (ret <= 0) {
> dev_err(dev, "Wrong data!\n");
> return ret;
This should probably be something like
return ret ? : -EINVAL;
or ret == 0 is not an error, and you should not display an error message
in that case.
Thanks,
Guenter
On Wed, Feb 18, 2015 at 11:07:30AM +0100, Lukasz Majewski wrote:
> Odroid U3 fan can work without being registered as OF cooling device
> (with CONFIG_THERMAL{_OF|} disabled).
> In this situation it can be controlled via PWM entry at
> /sys/class/hwmon/hwmon0/pwm1.
>
> Therefore, the thermal_cdev_update() function needs a stub
> to allow clean compilation.
I've just applied a patch on this same matter from Nishanth Menon [1].
Can you please check if his patch is enough for you?
[1] -
https://git.kernel.org/cgit/linux/kernel/git/evalenti/linux-soc-thermal.git/commit/?h=fixes&id=12ca7188468ee29c4e717f73db4bf43c90954fc7
>
> Signed-off-by: Lukasz Majewski <[email protected]>
> ---
> Changes for v2:
> - New patch
> Changes for v3:
> - thermal_cdev_update() now depends on CONFIG_THERMAL flag
> Changes for v4:
> - None
> ---
> include/linux/thermal.h | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/include/linux/thermal.h b/include/linux/thermal.h
> index eacf2de..25382e6 100644
> --- a/include/linux/thermal.h
> +++ b/include/linux/thermal.h
> @@ -298,6 +298,7 @@ struct thermal_cooling_device *
> thermal_of_cooling_device_register(struct device_node *np,
> char *type, void *devdata,
> const struct thermal_cooling_device_ops *);
> +void thermal_cdev_update(struct thermal_cooling_device *);
> #else
> static inline struct thermal_cooling_device *
> thermal_of_cooling_device_register(struct device_node *np,
> @@ -306,6 +307,9 @@ thermal_of_cooling_device_register(struct device_node *np,
> {
> return NULL;
> }
> +static inline void thermal_cdev_update(struct thermal_cooling_device *cdev)
> +{
> +}
> #endif
> #ifdef CONFIG_THERMAL_OF
> struct thermal_zone_device *
> @@ -349,7 +353,6 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp);
> int get_tz_trend(struct thermal_zone_device *, int);
> struct thermal_instance *get_thermal_instance(struct thermal_zone_device *,
> struct thermal_cooling_device *, int);
> -void thermal_cdev_update(struct thermal_cooling_device *);
> void thermal_notify_framework(struct thermal_zone_device *, int);
>
> #ifdef CONFIG_NET
> --
> 2.0.0.rc2
>
On Wed, Feb 18, 2015 at 11:07:29AM +0100, Lukasz Majewski wrote:
> Odroid U3 fan can work without being registered as OF cooling device
> (with CONFIG_THERMAL_OF disabled).
> In this situation it can be controlled via PWM entry at
> /sys/class/hwmon/hwmon0/pwm1.
>
> Therefore, the thermal_of_cooling_device_register() function needs a stub
> to allow clean compilation.
I've just applied a patch on this same matter from Nishanth Menon [1].
Can you please check if his patch is enough for you?
[1] -
https://git.kernel.org/cgit/linux/kernel/git/evalenti/linux-soc-thermal.git/commit/?h=fixes&id=12ca7188468ee29c4e717f73db4bf43c90954fc7
>
> Signed-off-by: Lukasz Majewski <[email protected]>
> ---
> Changes for v2:
> - None
> Changes for v3:
> - Provide stub declaration when CONFIG_THERMAL is not set
> Changes for v4:
> - None
> ---
> include/linux/thermal.h | 17 ++++++++++++++---
> 1 file changed, 14 insertions(+), 3 deletions(-)
>
> diff --git a/include/linux/thermal.h b/include/linux/thermal.h
> index fc52e30..eacf2de 100644
> --- a/include/linux/thermal.h
> +++ b/include/linux/thermal.h
> @@ -293,6 +293,20 @@ struct thermal_trip {
> };
>
> /* Function declarations */
> +#ifdef CONFIG_THERMAL
> +struct thermal_cooling_device *
> +thermal_of_cooling_device_register(struct device_node *np,
> + char *type, void *devdata,
> + const struct thermal_cooling_device_ops *);
> +#else
> +static inline struct thermal_cooling_device *
> +thermal_of_cooling_device_register(struct device_node *np,
> + char *type, void *devdata,
> + const struct thermal_cooling_device_ops *ops)
> +{
> + return NULL;
> +}
> +#endif
> #ifdef CONFIG_THERMAL_OF
> struct thermal_zone_device *
> thermal_zone_of_sensor_register(struct device *dev, int id, void *data,
> @@ -328,9 +342,6 @@ void thermal_zone_device_update(struct thermal_zone_device *);
>
> struct thermal_cooling_device *thermal_cooling_device_register(char *, void *,
> const struct thermal_cooling_device_ops *);
> -struct thermal_cooling_device *
> -thermal_of_cooling_device_register(struct device_node *np, char *, void *,
> - const struct thermal_cooling_device_ops *);
> void thermal_cooling_device_unregister(struct thermal_cooling_device *);
> struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name);
> int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp);
> --
> 2.0.0.rc2
>
On Wed, Feb 18, 2015 at 11:07:31AM +0100, Lukasz Majewski wrote:
> Explanation of several properties, which allow PWM fan working as a cooling
> device, have been embraced in this commit.
>
> Signed-off-by: Lukasz Majewski <[email protected]>
> ---
> Changes for v2:
> - Rename cooling-pwm-values to cooling-levels
> - Remove default-pulse-width property and stick to default hwmon policy
> Changes for v3:
> - Changing commit title from "hwmon: dts: Doc:" to "Documentation: dts"
> - Remove unnecessary properties
> - Set maximal cooling level to 230 instead of 255
> Changes for v4:
> - None
> ---
> Documentation/devicetree/bindings/hwmon/pwm-fan.txt | 20 ++++++++++++++++++++
> 1 file changed, 20 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
> index 610757c..d53fe0c 100644
> --- a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
> +++ b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
> @@ -3,10 +3,30 @@ Bindings for a fan connected to the PWM lines
> Required properties:
> - compatible : "pwm-fan"
> - pwms : the PWM that is used to control the PWM fan
> +- cooling-levels : PWM duty cycle values in a range from 0 to 255
> + which correspond to thermal cooling states
> +
> +Thorough description of the following bindings:
> + cooling-min-state = <0>;
> + cooling-max-state = <3>;
> + #cooling-cells = <2>;
> + thermal-zone {
> + cpu_thermal: cpu-thermal {
> + cooling-maps {
> + map0 {
> + trip = <&cpu_alert1>;
> + cooling-device = <&fan0 0 1>;
> + };
> + };
> + };
I am fine if you leave the reference to thermal.txt biding description,
but I would prefer if you move the above lines to a proper example section.
> +
> +for PWM FAN used as cooling device can be found at:
> +./Documentation/devicetree/bindings/thermal/thermal.txt
>
> Example:
> pwm-fan {
> compatible = "pwm-fan";
> status = "okay";
> pwms = <&pwm 0 10000 0>;
> + cooling-levels = <0 102 170 230>;
> };
> --
> 2.0.0.rc2
>
On Wed, Feb 18, 2015 at 11:07:33AM +0100, Lukasz Majewski wrote:
> With those bindings it is possible to use pwm-fan device available in
> Odroid U3 as a cooling device.
>
> Signed-off-by: Lukasz Majewski <[email protected]>
> ---
> Changes for v2:
> - Rename cooling-pwm-values property to cooling-levels
> Changes for v3:
> - Change patch's topic to "ARM dts"
> - Reduce maximal cooling-level to 230 from 255
> Changes for v4:
> - None
> ---
> arch/arm/boot/dts/exynos4412-odroidu3.dts | 33 ++++++++++++++++++++++++++++++-
> 1 file changed, 32 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts
> index 60bd1e4..3e5df3e 100644
> --- a/arch/arm/boot/dts/exynos4412-odroidu3.dts
> +++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts
> @@ -32,11 +32,42 @@
> };
> };
>
> - pwm-fan {
> + fan0: pwm-fan {
> compatible = "pwm-fan";
> pwms = <&pwm 0 10000 0>;
> + cooling-min-state = <0>;
> + cooling-max-state = <3>;
> + #cooling-cells = <2>;
> + cooling-levels = <0 102 170 230>;
> status = "okay";
> };
> +
> + thermal-zones {
> + cpu_thermal: cpu-thermal {
This thermal zone misses the mandatory properties as per
Documentation/devicetree/bindings/thermal/thermal.txt
> + cooling-maps {
> + map0 {
> + trip = <&cpu_alert1>;
> + cooling-device = <&cpu0 7 7>;
> + };
> + map1 {
> + trip = <&cpu_alert2>;
> + cooling-device = <&cpu0 13 13>;
> + };
> + map2 {
> + trip = <&cpu_alert0>;
> + cooling-device = <&fan0 0 1>;
> + };
> + map3 {
> + trip = <&cpu_alert1>;
> + cooling-device = <&fan0 1 2>;
> + };
> + map4 {
> + trip = <&cpu_alert2>;
> + cooling-device = <&fan0 2 3>;
> + };
> + };
> + };
> + };
> };
>
> &pwm {
> --
> 2.0.0.rc2
>
Hi Eduardo,
> On Wed, Feb 18, 2015 at 11:07:29AM +0100, Lukasz Majewski wrote:
> > Odroid U3 fan can work without being registered as OF cooling device
> > (with CONFIG_THERMAL_OF disabled).
> > In this situation it can be controlled via PWM entry at
> > /sys/class/hwmon/hwmon0/pwm1.
> >
> > Therefore, the thermal_of_cooling_device_register() function needs
> > a stub to allow clean compilation.
>
>
> I've just applied a patch on this same matter from Nishanth Menon [1].
> Can you please check if his patch is enough for you?
>
> [1] -
> https://git.kernel.org/cgit/linux/kernel/git/evalenti/linux-soc-thermal.git/commit/?h=fixes&id=12ca7188468ee29c4e717f73db4bf43c90954fc7
>
I will prepare new PWM-FAN patch set with above changes in mind.
>
> >
> > Signed-off-by: Lukasz Majewski <[email protected]>
> > ---
> > Changes for v2:
> > - None
> > Changes for v3:
> > - Provide stub declaration when CONFIG_THERMAL is not set
> > Changes for v4:
> > - None
> > ---
> > include/linux/thermal.h | 17 ++++++++++++++---
> > 1 file changed, 14 insertions(+), 3 deletions(-)
> >
> > diff --git a/include/linux/thermal.h b/include/linux/thermal.h
> > index fc52e30..eacf2de 100644
> > --- a/include/linux/thermal.h
> > +++ b/include/linux/thermal.h
> > @@ -293,6 +293,20 @@ struct thermal_trip {
> > };
> >
> > /* Function declarations */
> > +#ifdef CONFIG_THERMAL
> > +struct thermal_cooling_device *
> > +thermal_of_cooling_device_register(struct device_node *np,
> > + char *type, void *devdata,
> > + const struct
> > thermal_cooling_device_ops *); +#else
> > +static inline struct thermal_cooling_device *
> > +thermal_of_cooling_device_register(struct device_node *np,
> > + char *type, void *devdata,
> > + const struct
> > thermal_cooling_device_ops *ops) +{
> > + return NULL;
> > +}
> > +#endif
> > #ifdef CONFIG_THERMAL_OF
> > struct thermal_zone_device *
> > thermal_zone_of_sensor_register(struct device *dev, int id, void
> > *data, @@ -328,9 +342,6 @@ void thermal_zone_device_update(struct
> > thermal_zone_device *);
> > struct thermal_cooling_device
> > *thermal_cooling_device_register(char *, void *, const struct
> > thermal_cooling_device_ops *); -struct thermal_cooling_device *
> > -thermal_of_cooling_device_register(struct device_node *np, char *,
> > void *,
> > - const struct
> > thermal_cooling_device_ops *); void
> > thermal_cooling_device_unregister(struct thermal_cooling_device *);
> > struct thermal_zone_device *thermal_zone_get_zone_by_name(const
> > char *name); int thermal_zone_get_temp(struct thermal_zone_device
> > *tz, unsigned long *temp); -- 2.0.0.rc2
> >
--
Best regards,
Lukasz Majewski
Samsung R&D Institute Poland (SRPOL) | Linux Platform Group
Hi Eduardo,
> On Wed, Feb 18, 2015 at 11:07:33AM +0100, Lukasz Majewski wrote:
> > With those bindings it is possible to use pwm-fan device available
> > in Odroid U3 as a cooling device.
> >
> > Signed-off-by: Lukasz Majewski <[email protected]>
> > ---
> > Changes for v2:
> > - Rename cooling-pwm-values property to cooling-levels
> > Changes for v3:
> > - Change patch's topic to "ARM dts"
> > - Reduce maximal cooling-level to 230 from 255
> > Changes for v4:
> > - None
> > ---
> > arch/arm/boot/dts/exynos4412-odroidu3.dts | 33
> > ++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1
> > deletion(-)
> >
> > diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts
> > b/arch/arm/boot/dts/exynos4412-odroidu3.dts index 60bd1e4..3e5df3e
> > 100644 --- a/arch/arm/boot/dts/exynos4412-odroidu3.dts
> > +++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts
> > @@ -32,11 +32,42 @@
> > };
> > };
> >
> > - pwm-fan {
> > + fan0: pwm-fan {
> > compatible = "pwm-fan";
> > pwms = <&pwm 0 10000 0>;
> > + cooling-min-state = <0>;
> > + cooling-max-state = <3>;
> > + #cooling-cells = <2>;
> > + cooling-levels = <0 102 170 230>;
> > status = "okay";
> > };
> > +
> > + thermal-zones {
> > + cpu_thermal: cpu-thermal {
>
>
> This thermal zone misses the mandatory properties as per
> Documentation/devicetree/bindings/thermal/thermal.txt
Following mandatory properties:
thermal-sensors = <&tmu 0>;
polling-delay-passive = <0>;
polling-delay = <0>;
trips {
}
are defined in exynos4-cpu-thermal.dtsi included by this file.
In this patch only device dependent changes are stated.
>
> > + cooling-maps {
> > + map0 {
> > + trip = <&cpu_alert1>;
> > + cooling-device = <&cpu0 7 7>;
> > + };
> > + map1 {
> > + trip = <&cpu_alert2>;
> > + cooling-device = <&cpu0 13
> > 13>;
> > + };
> > + map2 {
> > + trip = <&cpu_alert0>;
> > + cooling-device = <&fan0 0 1>;
> > + };
> > + map3 {
> > + trip = <&cpu_alert1>;
> > + cooling-device = <&fan0 1 2>;
> > + };
> > + map4 {
> > + trip = <&cpu_alert2>;
> > + cooling-device = <&fan0 2 3>;
> > + };
> > + };
> > + };
> > + };
> > };
> >
> > &pwm {
> > --
> > 2.0.0.rc2
> >
--
Best regards,
Lukasz Majewski
Samsung R&D Institute Poland (SRPOL) | Linux Platform Group
Presented patches add support for Odroid's U3 optional CPU FAN, which uses PWM
subsystem for low level control.
After successful probe it registers itself as a cooling device for thermal
subsystem.
This driver also supports devices without DTS specified.
To provide correct functionality, new properties to device tree description for
Exynos4412 and in particular Odroid U3 have been added.
Those patches were tested at Exynos4412 - Odroid U3 board.
Patches were applied on:
linux-soc-thermal/fixes branch (Linux v4.0-rc1)
SHA1: b71d399c7f2fe06b60b96155ec0b9ae167334e4a
Kamil Debski (1):
ARM: dts: Add pwm-fan node to the Odroid-U3 board
Lukasz Majewski (5):
Documentation: dts: Documentation entry to explain how to use PWM FAN
as a cooling device
ARM: dts: Add properties to use pwm-fan device as a cooling device in
Odroid U3
hwmon: pwm-fan: Extract __set_pwm() function to only modify PWM duty
cycle
hwmon: pwm-fan: Read PWM FAN configuration from device tree
hwmon: pwm-fan: Code for using PWM FAN as a cooling device
.../devicetree/bindings/hwmon/pwm-fan.txt | 25 +++-
arch/arm/boot/dts/exynos4.dtsi | 2 +-
arch/arm/boot/dts/exynos4412-odroidu3.dts | 43 ++++++
drivers/hwmon/pwm-fan.c | 166 +++++++++++++++++++--
4 files changed, 220 insertions(+), 16 deletions(-)
--
2.0.0.rc2
Explanation of several properties, which allow PWM fan working as a cooling
device, have been embraced in this commit.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- Rename cooling-pwm-values to cooling-levels
- Remove default-pulse-width property and stick to default hwmon policy
Changes for v3:
- Changing commit title from "hwmon: dts: Doc:" to "Documentation: dts"
- Remove unnecessary properties
- Set maximal cooling level to 230 instead of 255
Changes for v4:
- None
Changes for v5:
- Move thermal-zones description to example section
- Extending example section
---
.../devicetree/bindings/hwmon/pwm-fan.txt | 25 +++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
index 610757c..645251b 100644
--- a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
+++ b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
@@ -3,10 +3,33 @@ Bindings for a fan connected to the PWM lines
Required properties:
- compatible : "pwm-fan"
- pwms : the PWM that is used to control the PWM fan
+- cooling-levels : PWM duty cycle values in a range from 0 to 255
+ which correspond to thermal cooling states
Example:
- pwm-fan {
+ fan0: pwm-fan {
compatible = "pwm-fan";
status = "okay";
pwms = <&pwm 0 10000 0>;
+ cooling-levels = <0 102 170 230>;
+ };
+
+ thermal-zones {
+ cpu_thermal: cpu-thermal {
+ cooling-min-state = <0>;
+ cooling-max-state = <3>;
+ #cooling-cells = <2>;
+ trips {
+ cpu_alert1: cpu-alert1 {
+ temperature = <100000>; /* millicelsius */
+ hysteresis = <2000>; /* millicelsius */
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ map0 {
+ trip = <&cpu_alert1>;
+ cooling-device = <&fan0 0 1>;
+ };
+ };
};
--
2.0.0.rc2
From: Kamil Debski <[email protected]>
Add pwm-fan node to the Odroid-U3 board file to enable PWM control of the
cooling fan. In addition, add the "pwm" label to the pwm@139D0000 node
in the exynos4412.dtsi.
Signed-off-by: Kamil Debski <[email protected]>
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes since v1:
- added pwm label to the pwm@139D0000 node in exynos4.dtsi
- use the pwm label in the exynos4412-odroidu3.dts
- change order or properties in the pwn-fan node, to be sorted
in alphabetical order
Changes for v5:
- Adding Signed-off-by: Lukasz Majewski <[email protected]>
- status = "okay"; dropped from the patch
---
arch/arm/boot/dts/exynos4.dtsi | 2 +-
arch/arm/boot/dts/exynos4412-odroidu3.dts | 12 ++++++++++++
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi
index 690f56c..a93f5ca5 100644
--- a/arch/arm/boot/dts/exynos4.dtsi
+++ b/arch/arm/boot/dts/exynos4.dtsi
@@ -599,7 +599,7 @@
status = "disabled";
};
- pwm@139D0000 {
+ pwm: pwm@139D0000 {
compatible = "samsung,exynos4210-pwm";
reg = <0x139D0000 0x1000>;
interrupts = <0 37 0>, <0 38 0>, <0 39 0>, <0 40 0>, <0 41 0>;
diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts
index 44684e5..4c04837 100644
--- a/arch/arm/boot/dts/exynos4412-odroidu3.dts
+++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts
@@ -31,6 +31,18 @@
linux,default-trigger = "heartbeat";
};
};
+
+ pwm-fan {
+ compatible = "pwm-fan";
+ pwms = <&pwm 0 10000 0>;
+ };
+};
+
+&pwm {
+ pinctrl-0 = <&pwm0_out>;
+ pinctrl-names = "default";
+ samsung,pwm-outputs = <0>;
+ status = "okay";
};
&usb3503 {
--
2.0.0.rc2
With those bindings it is possible to use pwm-fan device available in
Odroid U3 as a cooling device.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- Rename cooling-pwm-values property to cooling-levels
Changes for v3:
- Change patch's topic to "ARM dts"
- Reduce maximal cooling-level to 230 from 255
Changes for v4:
- None
Changes for v5:
- None
---
arch/arm/boot/dts/exynos4412-odroidu3.dts | 33 ++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts
index 4c04837..abcfa3c 100644
--- a/arch/arm/boot/dts/exynos4412-odroidu3.dts
+++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts
@@ -32,9 +32,40 @@
};
};
- pwm-fan {
+ fan0: pwm-fan {
compatible = "pwm-fan";
pwms = <&pwm 0 10000 0>;
+ cooling-min-state = <0>;
+ cooling-max-state = <3>;
+ #cooling-cells = <2>;
+ cooling-levels = <0 102 170 230>;
+ };
+
+ thermal-zones {
+ cpu_thermal: cpu-thermal {
+ cooling-maps {
+ map0 {
+ trip = <&cpu_alert1>;
+ cooling-device = <&cpu0 7 7>;
+ };
+ map1 {
+ trip = <&cpu_alert2>;
+ cooling-device = <&cpu0 13 13>;
+ };
+ map2 {
+ trip = <&cpu_alert0>;
+ cooling-device = <&fan0 0 1>;
+ };
+ map3 {
+ trip = <&cpu_alert1>;
+ cooling-device = <&fan0 1 2>;
+ };
+ map4 {
+ trip = <&cpu_alert2>;
+ cooling-device = <&fan0 2 3>;
+ };
+ };
+ };
};
};
--
2.0.0.rc2
It was necessary to decouple code handling writing to sysfs from the one
responsible for setting PWM of the fan.
Due to that, new __set_pwm() method was extracted, which is responsible for
only setting new PWM duty cycle.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- None
Changes for v3:
- The commit headline has been reedited.
Changes for v4:
- Protect "if (ctx->pwm_value == pwm)" with ctx lock
- Initialize ret with zero
Changes for v5:
- None
---
drivers/hwmon/pwm-fan.c | 33 +++++++++++++++++++++------------
1 file changed, 21 insertions(+), 12 deletions(-)
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 1991d903..bd42d39 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -33,20 +33,14 @@ struct pwm_fan_ctx {
unsigned char pwm_value;
};
-static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
{
- struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
- unsigned long pwm, duty;
- ssize_t ret;
-
- if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
- return -EINVAL;
+ unsigned long duty;
+ int ret = 0;
mutex_lock(&ctx->lock);
-
if (ctx->pwm_value == pwm)
- goto exit_set_pwm_no_change;
+ goto exit_set_pwm_err;
if (pwm == 0) {
pwm_disable(ctx->pwm);
@@ -66,13 +60,28 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
exit_set_pwm:
ctx->pwm_value = pwm;
-exit_set_pwm_no_change:
- ret = count;
exit_set_pwm_err:
mutex_unlock(&ctx->lock);
return ret;
}
+static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
+ unsigned long pwm;
+ int ret;
+
+ if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
+ return -EINVAL;
+
+ ret = __set_pwm(ctx, pwm);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
static ssize_t show_pwm(struct device *dev,
struct device_attribute *attr, char *buf)
{
--
2.0.0.rc2
This patch provides code for reading PWM FAN configuration data via
device tree. The pwm-fan can work with full speed when configuration
is not provided. However, errors are propagated when wrong DT bindings
are found.
Additionally the struct pwm_fan_ctx has been extended.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- Rename pwm_fan_max_states to pwm_fan_cooling_levels
- Moving pwm_fan_of_get_cooling_data() call after setting end enabling PWM FAN
- pwm_fan_of_get_cooling_data() now can fail - preserving old behaviour
- Remove unnecessary dev_err() call
Changes for v3:
- Patch's headline has been reedited
- pwm_fan_of_get_cooling_data() return code is now being checked.
- of_property_count_elems_of_size() is now used instead of_find_property()
- More verbose patch description added
Changes for v4:
- dev_err() has been removed from pwm_fan_of_get_cooling_data()
- Returning -EINVAL when "cooling-levels" are defined in DT, but doesn't
have the value
Changes for v5:
- Use of of_find_property() to assess if "cooling-levels" was defined in
device tree
- Replace of_property_count_elems_of_size() with of_property_count_u32_elems()
- Remove ambiguity with returning error code from of_property_count_u32_elems()
- Return -EINVAL when "cooling-levels" has zero elements
---
drivers/hwmon/pwm-fan.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 49 insertions(+), 1 deletion(-)
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index bd42d39..e6ed353 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -30,7 +30,10 @@
struct pwm_fan_ctx {
struct mutex lock;
struct pwm_device *pwm;
- unsigned char pwm_value;
+ unsigned int pwm_value;
+ unsigned int pwm_fan_state;
+ unsigned int pwm_fan_max_state;
+ unsigned int *pwm_fan_cooling_levels;
};
static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
@@ -100,6 +103,46 @@ static struct attribute *pwm_fan_attrs[] = {
ATTRIBUTE_GROUPS(pwm_fan);
+int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
+{
+ struct device_node *np = dev->of_node;
+ int num, i, ret;
+
+ if (!of_find_property(np, "cooling-levels", NULL))
+ return 0;
+
+ ret = of_property_count_u32_elems(np, "cooling-levels");
+ if (ret <= 0) {
+ dev_err(dev, "Wrong data!\n");
+ return ret ? : -EINVAL;
+ }
+
+ num = ret;
+ ctx->pwm_fan_cooling_levels = devm_kzalloc(dev, num * sizeof(u32),
+ GFP_KERNEL);
+ if (!ctx->pwm_fan_cooling_levels)
+ return -ENOMEM;
+
+ ret = of_property_read_u32_array(np, "cooling-levels",
+ ctx->pwm_fan_cooling_levels, num);
+ if (ret) {
+ dev_err(dev, "Property 'cooling-levels' cannot be read!\n");
+ return ret;
+ }
+
+ for (i = 0; i < num; i++) {
+ if (ctx->pwm_fan_cooling_levels[i] > MAX_PWM) {
+ dev_err(dev, "PWM fan state[%d]:%d > %d\n", i,
+ ctx->pwm_fan_cooling_levels[i], MAX_PWM);
+ return -EINVAL;
+ }
+ }
+
+ ctx->pwm_fan_max_state = num - 1;
+
+ return 0;
+}
+
static int pwm_fan_probe(struct platform_device *pdev)
{
struct device *hwmon;
@@ -145,6 +188,11 @@ static int pwm_fan_probe(struct platform_device *pdev)
pwm_disable(ctx->pwm);
return PTR_ERR(hwmon);
}
+
+ ret = pwm_fan_of_get_cooling_data(&pdev->dev, ctx);
+ if (ret)
+ return ret;
+
return 0;
}
--
2.0.0.rc2
The PWM FAN device can now be used as a thermal cooling device. Necessary
infrastructure has been added in this commit.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- Replace pwm_fan_cooling_states with pwm_fan_cooling_levels
- Update ctx->pwm_fan_state when correct data from device tree is available
- Using therma_cdev_update() when thermal is ready for controlling the fan
Changes for v3:
- Rename patch heading
- pwm_fan_of_get_cooling_data() now returns -EINVAL when no "cooling-levels"
property defined
- register of cooling device only when proper cooling data is present
Changes for v4:
- None
Changes for v5:
- Check for IS_ENABLED(CONFIG_THERMAL) has been added to prevent from
executing thermal_* specific functions
---
drivers/hwmon/pwm-fan.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 82 insertions(+), 1 deletion(-)
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index e6ed353..2c1a8f0 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -24,6 +24,7 @@
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/sysfs.h>
+#include <linux/thermal.h>
#define MAX_PWM 255
@@ -68,6 +69,17 @@ exit_set_pwm_err:
return ret;
}
+static void pwm_fan_update_state(struct pwm_fan_ctx *ctx, unsigned long pwm)
+{
+ int i;
+
+ for (i = 0; i < ctx->pwm_fan_max_state; ++i)
+ if (pwm < ctx->pwm_fan_cooling_levels[i + 1])
+ break;
+
+ ctx->pwm_fan_state = i;
+}
+
static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -82,6 +94,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
if (ret)
return ret;
+ pwm_fan_update_state(ctx, pwm);
return count;
}
@@ -103,6 +116,59 @@ static struct attribute *pwm_fan_attrs[] = {
ATTRIBUTE_GROUPS(pwm_fan);
+/* thermal cooling device callbacks */
+static int pwm_fan_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+
+ *state = ctx->pwm_fan_max_state;
+
+ return 0;
+}
+
+static int pwm_fan_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+
+ if (!ctx)
+ return -EINVAL;
+
+ *state = ctx->pwm_fan_state;
+
+ return 0;
+}
+
+static int
+pwm_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+ int ret;
+
+ if (!ctx || (state > ctx->pwm_fan_max_state))
+ return -EINVAL;
+
+ if (state == ctx->pwm_fan_state)
+ return 0;
+
+ ret = __set_pwm(ctx, ctx->pwm_fan_cooling_levels[state]);
+ if (ret) {
+ dev_err(&cdev->device, "Cannot set pwm!\n");
+ return ret;
+ }
+
+ ctx->pwm_fan_state = state;
+
+ return ret;
+}
+
+static const struct thermal_cooling_device_ops pwm_fan_cooling_ops = {
+ .get_max_state = pwm_fan_get_max_state,
+ .get_cur_state = pwm_fan_get_cur_state,
+ .set_cur_state = pwm_fan_set_cur_state,
+};
+
int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
{
struct device_node *np = dev->of_node;
@@ -145,8 +211,9 @@ int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
static int pwm_fan_probe(struct platform_device *pdev)
{
- struct device *hwmon;
+ struct thermal_cooling_device *cdev;
struct pwm_fan_ctx *ctx;
+ struct device *hwmon;
int duty_cycle;
int ret;
@@ -193,6 +260,20 @@ static int pwm_fan_probe(struct platform_device *pdev)
if (ret)
return ret;
+ ctx->pwm_fan_state = ctx->pwm_fan_max_state;
+ if (IS_ENABLED(CONFIG_THERMAL)) {
+ cdev = thermal_of_cooling_device_register(pdev->dev.of_node,
+ "pwm-fan", ctx,
+ &pwm_fan_cooling_ops);
+ if (IS_ERR(cdev)) {
+ dev_err(&pdev->dev,
+ "Failed to register pwm-fan as cooling device");
+ pwm_disable(ctx->pwm);
+ return PTR_ERR(cdev);
+ }
+ thermal_cdev_update(cdev);
+ }
+
return 0;
}
--
2.0.0.rc2
On Wed, Feb 25, 2015 at 04:34:16PM +0100, Lukasz Majewski wrote:
> Presented patches add support for Odroid's U3 optional CPU FAN, which uses PWM
> subsystem for low level control.
>
> After successful probe it registers itself as a cooling device for thermal
> subsystem.
>
> This driver also supports devices without DTS specified.
>
> To provide correct functionality, new properties to device tree description for
> Exynos4412 and in particular Odroid U3 have been added.
>
> Those patches were tested at Exynos4412 - Odroid U3 board.
>
> Patches were applied on:
> linux-soc-thermal/fixes branch (Linux v4.0-rc1)
> SHA1: b71d399c7f2fe06b60b96155ec0b9ae167334e4a
>
> Kamil Debski (1):
> ARM: dts: Add pwm-fan node to the Odroid-U3 board
>
> Lukasz Majewski (5):
> Documentation: dts: Documentation entry to explain how to use PWM FAN
> as a cooling device
> ARM: dts: Add properties to use pwm-fan device as a cooling device in
> Odroid U3
> hwmon: pwm-fan: Extract __set_pwm() function to only modify PWM duty
> cycle
> hwmon: pwm-fan: Read PWM FAN configuration from device tree
> hwmon: pwm-fan: Code for using PWM FAN as a cooling device
>
> .../devicetree/bindings/hwmon/pwm-fan.txt | 25 +++-
> arch/arm/boot/dts/exynos4.dtsi | 2 +-
> arch/arm/boot/dts/exynos4412-odroidu3.dts | 43 ++++++
> drivers/hwmon/pwm-fan.c | 166 +++++++++++++++++++--
> 4 files changed, 220 insertions(+), 16 deletions(-)
>
For the series:
Acked-by: Guenter Roeck <[email protected]>
Should I take it through hwmon ? Might make sense given the majority
of the changes is in hwmon code.
Thanks,
Guenter
Hey Lukasz,
On Wed, Feb 25, 2015 at 04:34:22PM +0100, Lukasz Majewski wrote:
> The PWM FAN device can now be used as a thermal cooling device. Necessary
> infrastructure has been added in this commit.
>
> Signed-off-by: Lukasz Majewski <[email protected]>
> ---
> Changes for v2:
> - Replace pwm_fan_cooling_states with pwm_fan_cooling_levels
> - Update ctx->pwm_fan_state when correct data from device tree is available
> - Using therma_cdev_update() when thermal is ready for controlling the fan
> Changes for v3:
> - Rename patch heading
> - pwm_fan_of_get_cooling_data() now returns -EINVAL when no "cooling-levels"
> property defined
> - register of cooling device only when proper cooling data is present
> Changes for v4:
> - None
> Changes for v5:
> - Check for IS_ENABLED(CONFIG_THERMAL) has been added to prevent from
> executing thermal_* specific functions
> ---
> drivers/hwmon/pwm-fan.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 82 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
> index e6ed353..2c1a8f0 100644
> --- a/drivers/hwmon/pwm-fan.c
> +++ b/drivers/hwmon/pwm-fan.c
> @@ -24,6 +24,7 @@
> #include <linux/platform_device.h>
> #include <linux/pwm.h>
> #include <linux/sysfs.h>
> +#include <linux/thermal.h>
>
> #define MAX_PWM 255
>
> @@ -68,6 +69,17 @@ exit_set_pwm_err:
> return ret;
> }
>
> +static void pwm_fan_update_state(struct pwm_fan_ctx *ctx, unsigned long pwm)
> +{
> + int i;
> +
> + for (i = 0; i < ctx->pwm_fan_max_state; ++i)
> + if (pwm < ctx->pwm_fan_cooling_levels[i + 1])
> + break;
> +
> + ctx->pwm_fan_state = i;
> +}
> +
> static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
> const char *buf, size_t count)
> {
> @@ -82,6 +94,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
> if (ret)
> return ret;
>
> + pwm_fan_update_state(ctx, pwm);
> return count;
> }
>
> @@ -103,6 +116,59 @@ static struct attribute *pwm_fan_attrs[] = {
>
> ATTRIBUTE_GROUPS(pwm_fan);
>
> +/* thermal cooling device callbacks */
> +static int pwm_fan_get_max_state(struct thermal_cooling_device *cdev,
> + unsigned long *state)
> +{
> + struct pwm_fan_ctx *ctx = cdev->devdata;
> +
Why this call back is the only one you didn't add a check for ctx ==
NULL?
> + *state = ctx->pwm_fan_max_state;
> +
> + return 0;
> +}
> +
> +static int pwm_fan_get_cur_state(struct thermal_cooling_device *cdev,
> + unsigned long *state)
> +{
> + struct pwm_fan_ctx *ctx = cdev->devdata;
> +
> + if (!ctx)
> + return -EINVAL;
> +
> + *state = ctx->pwm_fan_state;
> +
> + return 0;
> +}
> +
> +static int
> +pwm_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
> +{
> + struct pwm_fan_ctx *ctx = cdev->devdata;
> + int ret;
> +
> + if (!ctx || (state > ctx->pwm_fan_max_state))
> + return -EINVAL;
> +
> + if (state == ctx->pwm_fan_state)
> + return 0;
> +
> + ret = __set_pwm(ctx, ctx->pwm_fan_cooling_levels[state]);
> + if (ret) {
> + dev_err(&cdev->device, "Cannot set pwm!\n");
> + return ret;
> + }
> +
> + ctx->pwm_fan_state = state;
> +
> + return ret;
> +}
> +
> +static const struct thermal_cooling_device_ops pwm_fan_cooling_ops = {
> + .get_max_state = pwm_fan_get_max_state,
> + .get_cur_state = pwm_fan_get_cur_state,
> + .set_cur_state = pwm_fan_set_cur_state,
> +};
> +
> int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
> {
> struct device_node *np = dev->of_node;
> @@ -145,8 +211,9 @@ int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
>
> static int pwm_fan_probe(struct platform_device *pdev)
> {
> - struct device *hwmon;
> + struct thermal_cooling_device *cdev;
> struct pwm_fan_ctx *ctx;
> + struct device *hwmon;
> int duty_cycle;
> int ret;
>
> @@ -193,6 +260,20 @@ static int pwm_fan_probe(struct platform_device *pdev)
> if (ret)
> return ret;
>
> + ctx->pwm_fan_state = ctx->pwm_fan_max_state;
> + if (IS_ENABLED(CONFIG_THERMAL)) {
> + cdev = thermal_of_cooling_device_register(pdev->dev.of_node,
> + "pwm-fan", ctx,
> + &pwm_fan_cooling_ops);
I know this is a PITA, but thermal subsystem still does not use devm
based helpers. That means, you need to be old school and unregister the
device yourself.
Please add the cdev to ctx structure:
+ ctx->cdev = cdev;
and add a call for:
thermal_cooling_device_unregister(ctx->cdev);
in static int pwm_fan_remove(struct platform_device *pdev)
despite the above two minor issues, you may add my
Acked-by: Eduardo Valentin <[email protected]>
> + if (IS_ERR(cdev)) {
> + dev_err(&pdev->dev,
> + "Failed to register pwm-fan as cooling device");
> + pwm_disable(ctx->pwm);
> + return PTR_ERR(cdev);
> + }
> + thermal_cdev_update(cdev);
> + }
> +
> return 0;
> }
>
> --
> 2.0.0.rc2
>
Guenter,
On Wed, Feb 25, 2015 at 09:18:20AM -0800, Guenter Roeck wrote:
> On Wed, Feb 25, 2015 at 04:34:16PM +0100, Lukasz Majewski wrote:
> > Presented patches add support for Odroid's U3 optional CPU FAN, which uses PWM
> > subsystem for low level control.
> >
> > After successful probe it registers itself as a cooling device for thermal
> > subsystem.
> >
> > This driver also supports devices without DTS specified.
> >
> > To provide correct functionality, new properties to device tree description for
> > Exynos4412 and in particular Odroid U3 have been added.
> >
> > Those patches were tested at Exynos4412 - Odroid U3 board.
> >
> > Patches were applied on:
> > linux-soc-thermal/fixes branch (Linux v4.0-rc1)
> > SHA1: b71d399c7f2fe06b60b96155ec0b9ae167334e4a
> >
> > Kamil Debski (1):
> > ARM: dts: Add pwm-fan node to the Odroid-U3 board
> >
> > Lukasz Majewski (5):
> > Documentation: dts: Documentation entry to explain how to use PWM FAN
> > as a cooling device
> > ARM: dts: Add properties to use pwm-fan device as a cooling device in
> > Odroid U3
> > hwmon: pwm-fan: Extract __set_pwm() function to only modify PWM duty
> > cycle
> > hwmon: pwm-fan: Read PWM FAN configuration from device tree
> > hwmon: pwm-fan: Code for using PWM FAN as a cooling device
> >
> > .../devicetree/bindings/hwmon/pwm-fan.txt | 25 +++-
> > arch/arm/boot/dts/exynos4.dtsi | 2 +-
> > arch/arm/boot/dts/exynos4412-odroidu3.dts | 43 ++++++
> > drivers/hwmon/pwm-fan.c | 166 +++++++++++++++++++--
> > 4 files changed, 220 insertions(+), 16 deletions(-)
> >
> For the series:
>
> Acked-by: Guenter Roeck <[email protected]>
>
> Should I take it through hwmon ? Might make sense given the majority
> of the changes is in hwmon code.
I believe the series should go via your tree, yes. I had only minor
comments in the code added for the cooling device code, as it lacks the
unregistration call in the .remove callback.
Also, the DTS changes may generate conflicts with platform code. Lukasz
may probably ask Kukjin Kim to add them via the samsung tree.
BR,
Eduardo Valentin
>
> Thanks,
> Guenter
On Wed, Feb 25, 2015 at 02:28:15PM +0100, Lukasz Majewski wrote:
> Hi Eduardo,
>
> > On Wed, Feb 18, 2015 at 11:07:33AM +0100, Lukasz Majewski wrote:
> > > With those bindings it is possible to use pwm-fan device available
> > > in Odroid U3 as a cooling device.
> > >
> > > Signed-off-by: Lukasz Majewski <[email protected]>
> > > ---
> > > Changes for v2:
> > > - Rename cooling-pwm-values property to cooling-levels
> > > Changes for v3:
> > > - Change patch's topic to "ARM dts"
> > > - Reduce maximal cooling-level to 230 from 255
> > > Changes for v4:
> > > - None
> > > ---
> > > arch/arm/boot/dts/exynos4412-odroidu3.dts | 33
> > > ++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1
> > > deletion(-)
> > >
> > > diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts
> > > b/arch/arm/boot/dts/exynos4412-odroidu3.dts index 60bd1e4..3e5df3e
> > > 100644 --- a/arch/arm/boot/dts/exynos4412-odroidu3.dts
> > > +++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts
> > > @@ -32,11 +32,42 @@
> > > };
> > > };
> > >
> > > - pwm-fan {
> > > + fan0: pwm-fan {
> > > compatible = "pwm-fan";
> > > pwms = <&pwm 0 10000 0>;
> > > + cooling-min-state = <0>;
> > > + cooling-max-state = <3>;
> > > + #cooling-cells = <2>;
> > > + cooling-levels = <0 102 170 230>;
> > > status = "okay";
> > > };
> > > +
> > > + thermal-zones {
> > > + cpu_thermal: cpu-thermal {
> >
> >
> > This thermal zone misses the mandatory properties as per
> > Documentation/devicetree/bindings/thermal/thermal.txt
>
> Following mandatory properties:
> thermal-sensors = <&tmu 0>;
> polling-delay-passive = <0>;
> polling-delay = <0>;
> trips {
>
> }
>
> are defined in exynos4-cpu-thermal.dtsi included by this file.
>
> In this patch only device dependent changes are stated.
OK. I missed that.
>
> >
> > > + cooling-maps {
> > > + map0 {
> > > + trip = <&cpu_alert1>;
> > > + cooling-device = <&cpu0 7 7>;
> > > + };
> > > + map1 {
> > > + trip = <&cpu_alert2>;
> > > + cooling-device = <&cpu0 13
> > > 13>;
> > > + };
> > > + map2 {
> > > + trip = <&cpu_alert0>;
> > > + cooling-device = <&fan0 0 1>;
> > > + };
> > > + map3 {
> > > + trip = <&cpu_alert1>;
> > > + cooling-device = <&fan0 1 2>;
> > > + };
> > > + map4 {
> > > + trip = <&cpu_alert2>;
> > > + cooling-device = <&fan0 2 3>;
> > > + };
> > > + };
> > > + };
> > > + };
> > > };
> > >
> > > &pwm {
> > > --
> > > 2.0.0.rc2
> > >
>
>
>
> --
> Best regards,
>
> Lukasz Majewski
>
> Samsung R&D Institute Poland (SRPOL) | Linux Platform Group
On Wed, Feb 25, 2015 at 02:29:18PM -0400, Eduardo Valentin wrote:
> Guenter,
>
> On Wed, Feb 25, 2015 at 09:18:20AM -0800, Guenter Roeck wrote:
> > On Wed, Feb 25, 2015 at 04:34:16PM +0100, Lukasz Majewski wrote:
> > > Presented patches add support for Odroid's U3 optional CPU FAN, which uses PWM
> > > subsystem for low level control.
> > >
> > > After successful probe it registers itself as a cooling device for thermal
> > > subsystem.
> > >
> > > This driver also supports devices without DTS specified.
> > >
> > > To provide correct functionality, new properties to device tree description for
> > > Exynos4412 and in particular Odroid U3 have been added.
> > >
> > > Those patches were tested at Exynos4412 - Odroid U3 board.
> > >
> > > Patches were applied on:
> > > linux-soc-thermal/fixes branch (Linux v4.0-rc1)
> > > SHA1: b71d399c7f2fe06b60b96155ec0b9ae167334e4a
> > >
> > > Kamil Debski (1):
> > > ARM: dts: Add pwm-fan node to the Odroid-U3 board
> > >
> > > Lukasz Majewski (5):
> > > Documentation: dts: Documentation entry to explain how to use PWM FAN
> > > as a cooling device
> > > ARM: dts: Add properties to use pwm-fan device as a cooling device in
> > > Odroid U3
> > > hwmon: pwm-fan: Extract __set_pwm() function to only modify PWM duty
> > > cycle
> > > hwmon: pwm-fan: Read PWM FAN configuration from device tree
> > > hwmon: pwm-fan: Code for using PWM FAN as a cooling device
> > >
> > > .../devicetree/bindings/hwmon/pwm-fan.txt | 25 +++-
> > > arch/arm/boot/dts/exynos4.dtsi | 2 +-
> > > arch/arm/boot/dts/exynos4412-odroidu3.dts | 43 ++++++
> > > drivers/hwmon/pwm-fan.c | 166 +++++++++++++++++++--
> > > 4 files changed, 220 insertions(+), 16 deletions(-)
> > >
> > For the series:
> >
> > Acked-by: Guenter Roeck <[email protected]>
> >
> > Should I take it through hwmon ? Might make sense given the majority
> > of the changes is in hwmon code.
>
> I believe the series should go via your tree, yes. I had only minor
> comments in the code added for the cooling device code, as it lacks the
> unregistration call in the .remove callback.
>
> Also, the DTS changes may generate conflicts with platform code. Lukasz
> may probably ask Kukjin Kim to add them via the samsung tree.
>
Ok, I'll wait for the updated patch and then add the hwmon parts to hwmon-next.
Thanks,
Guenter
On Wed, Feb 25, 2015 at 04:34:19PM +0100, Lukasz Majewski wrote:
> With those bindings it is possible to use pwm-fan device available in
> Odroid U3 as a cooling device.
>
> Signed-off-by: Lukasz Majewski <[email protected]>
Acked-by: Eduardo Valentin <[email protected]>
> ---
> Changes for v2:
> - Rename cooling-pwm-values property to cooling-levels
> Changes for v3:
> - Change patch's topic to "ARM dts"
> - Reduce maximal cooling-level to 230 from 255
> Changes for v4:
> - None
> Changes for v5:
> - None
> ---
> arch/arm/boot/dts/exynos4412-odroidu3.dts | 33 ++++++++++++++++++++++++++++++-
> 1 file changed, 32 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts
> index 4c04837..abcfa3c 100644
> --- a/arch/arm/boot/dts/exynos4412-odroidu3.dts
> +++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts
> @@ -32,9 +32,40 @@
> };
> };
>
> - pwm-fan {
> + fan0: pwm-fan {
> compatible = "pwm-fan";
> pwms = <&pwm 0 10000 0>;
> + cooling-min-state = <0>;
> + cooling-max-state = <3>;
> + #cooling-cells = <2>;
> + cooling-levels = <0 102 170 230>;
> + };
> +
> + thermal-zones {
> + cpu_thermal: cpu-thermal {
> + cooling-maps {
> + map0 {
> + trip = <&cpu_alert1>;
> + cooling-device = <&cpu0 7 7>;
> + };
> + map1 {
> + trip = <&cpu_alert2>;
> + cooling-device = <&cpu0 13 13>;
> + };
> + map2 {
> + trip = <&cpu_alert0>;
> + cooling-device = <&fan0 0 1>;
> + };
> + map3 {
> + trip = <&cpu_alert1>;
> + cooling-device = <&fan0 1 2>;
> + };
> + map4 {
> + trip = <&cpu_alert2>;
> + cooling-device = <&fan0 2 3>;
> + };
> + };
> + };
> };
> };
>
> --
> 2.0.0.rc2
>
On Wed, Feb 25, 2015 at 04:34:17PM +0100, Lukasz Majewski wrote:
> Explanation of several properties, which allow PWM fan working as a cooling
> device, have been embraced in this commit.
>
> Signed-off-by: Lukasz Majewski <[email protected]>
> ---
> Changes for v2:
> - Rename cooling-pwm-values to cooling-levels
> - Remove default-pulse-width property and stick to default hwmon policy
> Changes for v3:
> - Changing commit title from "hwmon: dts: Doc:" to "Documentation: dts"
> - Remove unnecessary properties
> - Set maximal cooling level to 230 instead of 255
> Changes for v4:
> - None
> Changes for v5:
> - Move thermal-zones description to example section
> - Extending example section
>
> ---
> .../devicetree/bindings/hwmon/pwm-fan.txt | 25 +++++++++++++++++++++-
> 1 file changed, 24 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
> index 610757c..645251b 100644
> --- a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
> +++ b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
> @@ -3,10 +3,33 @@ Bindings for a fan connected to the PWM lines
> Required properties:
> - compatible : "pwm-fan"
> - pwms : the PWM that is used to control the PWM fan
> +- cooling-levels : PWM duty cycle values in a range from 0 to 255
> + which correspond to thermal cooling states
>
> Example:
> - pwm-fan {
> + fan0: pwm-fan {
> compatible = "pwm-fan";
> status = "okay";
> pwms = <&pwm 0 10000 0>;
> + cooling-levels = <0 102 170 230>;
> + };
> +
> + thermal-zones {
> + cpu_thermal: cpu-thermal {
> + cooling-min-state = <0>;
> + cooling-max-state = <3>;
> + #cooling-cells = <2>;
> + trips {
> + cpu_alert1: cpu-alert1 {
> + temperature = <100000>; /* millicelsius */
> + hysteresis = <2000>; /* millicelsius */
> + type = "passive";
> + };
> + };
> + cooling-maps {
> + map0 {
> + trip = <&cpu_alert1>;
> + cooling-device = <&fan0 0 1>;
> + };
> + };
The above example has two issues:
(1) it is wrong, as the cooling device properties are in the thermal
zone node.
(2) has a misalignment / tabulation
Can you please fix that in your next version too?
> };
> --
> 2.0.0.rc2
>
On Wed, Feb 25, 2015 at 03:11:24PM -0400, Eduardo Valentin wrote:
> On Wed, Feb 25, 2015 at 04:34:17PM +0100, Lukasz Majewski wrote:
> > Explanation of several properties, which allow PWM fan working as a cooling
> > device, have been embraced in this commit.
> >
> > Signed-off-by: Lukasz Majewski <[email protected]>
> > ---
> > Changes for v2:
> > - Rename cooling-pwm-values to cooling-levels
> > - Remove default-pulse-width property and stick to default hwmon policy
> > Changes for v3:
> > - Changing commit title from "hwmon: dts: Doc:" to "Documentation: dts"
> > - Remove unnecessary properties
> > - Set maximal cooling level to 230 instead of 255
> > Changes for v4:
> > - None
> > Changes for v5:
> > - Move thermal-zones description to example section
> > - Extending example section
> >
> > ---
> > .../devicetree/bindings/hwmon/pwm-fan.txt | 25 +++++++++++++++++++++-
> > 1 file changed, 24 insertions(+), 1 deletion(-)
> >
> > diff --git a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
> > index 610757c..645251b 100644
> > --- a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
> > +++ b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
> > @@ -3,10 +3,33 @@ Bindings for a fan connected to the PWM lines
> > Required properties:
> > - compatible : "pwm-fan"
> > - pwms : the PWM that is used to control the PWM fan
> > +- cooling-levels : PWM duty cycle values in a range from 0 to 255
> > + which correspond to thermal cooling states
> >
> > Example:
> > - pwm-fan {
> > + fan0: pwm-fan {
> > compatible = "pwm-fan";
> > status = "okay";
> > pwms = <&pwm 0 10000 0>;
> > + cooling-levels = <0 102 170 230>;
> > + };
> > +
> > + thermal-zones {
> > + cpu_thermal: cpu-thermal {
> > + cooling-min-state = <0>;
> > + cooling-max-state = <3>;
> > + #cooling-cells = <2>;
> > + trips {
> > + cpu_alert1: cpu-alert1 {
> > + temperature = <100000>; /* millicelsius */
> > + hysteresis = <2000>; /* millicelsius */
> > + type = "passive";
> > + };
> > + };
> > + cooling-maps {
> > + map0 {
> > + trip = <&cpu_alert1>;
> > + cooling-device = <&fan0 0 1>;
> > + };
> > + };
>
> The above example has two issues:
> (1) it is wrong, as the cooling device properties are in the thermal
> zone node.
> (2) has a misalignment / tabulation
>
> Can you please fix that in your next version too?
Apart from the above two issues you may add my
Acked-by: Eduardo Valentin <[email protected]>
>
> > };
> > --
> > 2.0.0.rc2
> >
Presented patches add support for Odroid's U3 optional CPU FAN, which uses PWM
subsystem for low level control.
After successful probe it registers itself as a cooling device for thermal
subsystem.
This driver also supports devices without DTS specified.
To provide correct functionality, new properties to device tree description for
Exynos4412 and in particular Odroid U3 have been added.
Those patches were tested at Exynos4412 - Odroid U3 board.
Patches were applied on:
linux-soc-thermal/fixes branch (Linux v4.0-rc1)
SHA1: b71d399c7f2fe06b60b96155ec0b9ae167334e4a
Kamil Debski (1):
ARM: dts: Add pwm-fan node to the Odroid-U3 board
Lukasz Majewski (5):
Documentation: dts: Documentation entry to explain how to use PWM FAN
as a cooling device
ARM: dts: Add properties to use pwm-fan device as a cooling device in
Odroid U3
hwmon: pwm-fan: Extract __set_pwm() function to only modify PWM duty
cycle
hwmon: pwm-fan: Read PWM FAN configuration from device tree
hwmon: pwm-fan: Code for using PWM FAN as a cooling device
.../devicetree/bindings/hwmon/pwm-fan.txt | 29 +++-
arch/arm/boot/dts/exynos4.dtsi | 2 +-
arch/arm/boot/dts/exynos4412-odroidu3.dts | 43 ++++++
drivers/hwmon/pwm-fan.c | 172 +++++++++++++++++++--
4 files changed, 229 insertions(+), 17 deletions(-)
--
2.0.0.rc2
Explanation of several properties, which allow PWM fan working as a cooling
device, have been embraced in this commit.
Signed-off-by: Lukasz Majewski <[email protected]>
Acked-by: Eduardo Valentin <[email protected]>
---
Changes for v2:
- Rename cooling-pwm-values to cooling-levels
- Remove default-pulse-width property and stick to default hwmon policy
Changes for v3:
- Changing commit title from "hwmon: dts: Doc:" to "Documentation: dts"
- Remove unnecessary properties
- Set maximal cooling level to 230 instead of 255
Changes for v4:
- None
Changes for v5:
- Move thermal-zones description to example section
- Extending example section
Changes for v6:
- cooling-{min|max}-state properties added to pwm-fan binding
- Mandatory properties for thermal-zones added
- Indentation fixed
---
.../devicetree/bindings/hwmon/pwm-fan.txt | 29 ++++++++++++++++++++--
1 file changed, 27 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
index 610757c..c6d5332 100644
--- a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
+++ b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
@@ -3,10 +3,35 @@ Bindings for a fan connected to the PWM lines
Required properties:
- compatible : "pwm-fan"
- pwms : the PWM that is used to control the PWM fan
+- cooling-levels : PWM duty cycle values in a range from 0 to 255
+ which correspond to thermal cooling states
Example:
- pwm-fan {
+ fan0: pwm-fan {
compatible = "pwm-fan";
- status = "okay";
+ cooling-min-state = <0>;
+ cooling-max-state = <3>;
+ #cooling-cells = <2>;
pwms = <&pwm 0 10000 0>;
+ cooling-levels = <0 102 170 230>;
};
+
+ thermal-zones {
+ cpu_thermal: cpu-thermal {
+ thermal-sensors = <&tmu 0>;
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ trips {
+ cpu_alert1: cpu-alert1 {
+ temperature = <100000>; /* millicelsius */
+ hysteresis = <2000>; /* millicelsius */
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ map0 {
+ trip = <&cpu_alert1>;
+ cooling-device = <&fan0 0 1>;
+ };
+ };
+ };
--
2.0.0.rc2
From: Kamil Debski <[email protected]>
Add pwm-fan node to the Odroid-U3 board file to enable PWM control of the
cooling fan. In addition, add the "pwm" label to the pwm@139D0000 node
in the exynos4412.dtsi.
Signed-off-by: Kamil Debski <[email protected]>
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes since v1:
- added pwm label to the pwm@139D0000 node in exynos4.dtsi
- use the pwm label in the exynos4412-odroidu3.dts
- change order or properties in the pwn-fan node, to be sorted
in alphabetical order
Changes for v5:
- Adding Signed-off-by: Lukasz Majewski <[email protected]>
- status = "okay"; dropped from the patch
Changes for v6:
- None
---
arch/arm/boot/dts/exynos4.dtsi | 2 +-
arch/arm/boot/dts/exynos4412-odroidu3.dts | 12 ++++++++++++
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi
index 690f56c..a93f5ca5 100644
--- a/arch/arm/boot/dts/exynos4.dtsi
+++ b/arch/arm/boot/dts/exynos4.dtsi
@@ -599,7 +599,7 @@
status = "disabled";
};
- pwm@139D0000 {
+ pwm: pwm@139D0000 {
compatible = "samsung,exynos4210-pwm";
reg = <0x139D0000 0x1000>;
interrupts = <0 37 0>, <0 38 0>, <0 39 0>, <0 40 0>, <0 41 0>;
diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts
index 44684e5..4c04837 100644
--- a/arch/arm/boot/dts/exynos4412-odroidu3.dts
+++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts
@@ -31,6 +31,18 @@
linux,default-trigger = "heartbeat";
};
};
+
+ pwm-fan {
+ compatible = "pwm-fan";
+ pwms = <&pwm 0 10000 0>;
+ };
+};
+
+&pwm {
+ pinctrl-0 = <&pwm0_out>;
+ pinctrl-names = "default";
+ samsung,pwm-outputs = <0>;
+ status = "okay";
};
&usb3503 {
--
2.0.0.rc2
With those bindings it is possible to use pwm-fan device available in
Odroid U3 as a cooling device.
Signed-off-by: Lukasz Majewski <[email protected]>
Acked-by: Eduardo Valentin <[email protected]>
---
Changes for v2:
- Rename cooling-pwm-values property to cooling-levels
Changes for v3:
- Change patch's topic to "ARM dts"
- Reduce maximal cooling-level to 230 from 255
Changes for v4:
- None
Changes for v5:
- None
Changes for v6:
- None
---
arch/arm/boot/dts/exynos4412-odroidu3.dts | 33 ++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts
index 4c04837..abcfa3c 100644
--- a/arch/arm/boot/dts/exynos4412-odroidu3.dts
+++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts
@@ -32,9 +32,40 @@
};
};
- pwm-fan {
+ fan0: pwm-fan {
compatible = "pwm-fan";
pwms = <&pwm 0 10000 0>;
+ cooling-min-state = <0>;
+ cooling-max-state = <3>;
+ #cooling-cells = <2>;
+ cooling-levels = <0 102 170 230>;
+ };
+
+ thermal-zones {
+ cpu_thermal: cpu-thermal {
+ cooling-maps {
+ map0 {
+ trip = <&cpu_alert1>;
+ cooling-device = <&cpu0 7 7>;
+ };
+ map1 {
+ trip = <&cpu_alert2>;
+ cooling-device = <&cpu0 13 13>;
+ };
+ map2 {
+ trip = <&cpu_alert0>;
+ cooling-device = <&fan0 0 1>;
+ };
+ map3 {
+ trip = <&cpu_alert1>;
+ cooling-device = <&fan0 1 2>;
+ };
+ map4 {
+ trip = <&cpu_alert2>;
+ cooling-device = <&fan0 2 3>;
+ };
+ };
+ };
};
};
--
2.0.0.rc2
It was necessary to decouple code handling writing to sysfs from the one
responsible for setting PWM of the fan.
Due to that, new __set_pwm() method was extracted, which is responsible for
only setting new PWM duty cycle.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- None
Changes for v3:
- The commit headline has been reedited.
Changes for v4:
- Protect "if (ctx->pwm_value == pwm)" with ctx lock
- Initialize ret with zero
Changes for v5:
- None
Changes for v6:
- None
---
drivers/hwmon/pwm-fan.c | 33 +++++++++++++++++++++------------
1 file changed, 21 insertions(+), 12 deletions(-)
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 1991d903..bd42d39 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -33,20 +33,14 @@ struct pwm_fan_ctx {
unsigned char pwm_value;
};
-static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
{
- struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
- unsigned long pwm, duty;
- ssize_t ret;
-
- if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
- return -EINVAL;
+ unsigned long duty;
+ int ret = 0;
mutex_lock(&ctx->lock);
-
if (ctx->pwm_value == pwm)
- goto exit_set_pwm_no_change;
+ goto exit_set_pwm_err;
if (pwm == 0) {
pwm_disable(ctx->pwm);
@@ -66,13 +60,28 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
exit_set_pwm:
ctx->pwm_value = pwm;
-exit_set_pwm_no_change:
- ret = count;
exit_set_pwm_err:
mutex_unlock(&ctx->lock);
return ret;
}
+static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
+ unsigned long pwm;
+ int ret;
+
+ if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
+ return -EINVAL;
+
+ ret = __set_pwm(ctx, pwm);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
static ssize_t show_pwm(struct device *dev,
struct device_attribute *attr, char *buf)
{
--
2.0.0.rc2
This patch provides code for reading PWM FAN configuration data via
device tree. The pwm-fan can work with full speed when configuration
is not provided. However, errors are propagated when wrong DT bindings
are found.
Additionally the struct pwm_fan_ctx has been extended.
Signed-off-by: Lukasz Majewski <[email protected]>
---
Changes for v2:
- Rename pwm_fan_max_states to pwm_fan_cooling_levels
- Moving pwm_fan_of_get_cooling_data() call after setting end enabling PWM FAN
- pwm_fan_of_get_cooling_data() now can fail - preserving old behaviour
- Remove unnecessary dev_err() call
Changes for v3:
- Patch's headline has been reedited
- pwm_fan_of_get_cooling_data() return code is now being checked.
- of_property_count_elems_of_size() is now used instead of_find_property()
- More verbose patch description added
Changes for v4:
- dev_err() has been removed from pwm_fan_of_get_cooling_data()
- Returning -EINVAL when "cooling-levels" are defined in DT, but doesn't
have the value
Changes for v5:
- Use of of_find_property() to assess if "cooling-levels" was defined in
device tree
- Replace of_property_count_elems_of_size() with of_property_count_u32_elems()
- Remove ambiguity with returning error code from of_property_count_u32_elems()
- Return -EINVAL when "cooling-levels" has zero elements
Changes for v6:
- None
---
drivers/hwmon/pwm-fan.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 49 insertions(+), 1 deletion(-)
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index bd42d39..e6ed353 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -30,7 +30,10 @@
struct pwm_fan_ctx {
struct mutex lock;
struct pwm_device *pwm;
- unsigned char pwm_value;
+ unsigned int pwm_value;
+ unsigned int pwm_fan_state;
+ unsigned int pwm_fan_max_state;
+ unsigned int *pwm_fan_cooling_levels;
};
static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
@@ -100,6 +103,46 @@ static struct attribute *pwm_fan_attrs[] = {
ATTRIBUTE_GROUPS(pwm_fan);
+int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
+{
+ struct device_node *np = dev->of_node;
+ int num, i, ret;
+
+ if (!of_find_property(np, "cooling-levels", NULL))
+ return 0;
+
+ ret = of_property_count_u32_elems(np, "cooling-levels");
+ if (ret <= 0) {
+ dev_err(dev, "Wrong data!\n");
+ return ret ? : -EINVAL;
+ }
+
+ num = ret;
+ ctx->pwm_fan_cooling_levels = devm_kzalloc(dev, num * sizeof(u32),
+ GFP_KERNEL);
+ if (!ctx->pwm_fan_cooling_levels)
+ return -ENOMEM;
+
+ ret = of_property_read_u32_array(np, "cooling-levels",
+ ctx->pwm_fan_cooling_levels, num);
+ if (ret) {
+ dev_err(dev, "Property 'cooling-levels' cannot be read!\n");
+ return ret;
+ }
+
+ for (i = 0; i < num; i++) {
+ if (ctx->pwm_fan_cooling_levels[i] > MAX_PWM) {
+ dev_err(dev, "PWM fan state[%d]:%d > %d\n", i,
+ ctx->pwm_fan_cooling_levels[i], MAX_PWM);
+ return -EINVAL;
+ }
+ }
+
+ ctx->pwm_fan_max_state = num - 1;
+
+ return 0;
+}
+
static int pwm_fan_probe(struct platform_device *pdev)
{
struct device *hwmon;
@@ -145,6 +188,11 @@ static int pwm_fan_probe(struct platform_device *pdev)
pwm_disable(ctx->pwm);
return PTR_ERR(hwmon);
}
+
+ ret = pwm_fan_of_get_cooling_data(&pdev->dev, ctx);
+ if (ret)
+ return ret;
+
return 0;
}
--
2.0.0.rc2
The PWM FAN device can now be used as a thermal cooling device. Necessary
infrastructure has been added in this commit.
Signed-off-by: Lukasz Majewski <[email protected]>
Acked-by: Eduardo Valentin <[email protected]>
---
Changes for v2:
- Replace pwm_fan_cooling_states with pwm_fan_cooling_levels
- Update ctx->pwm_fan_state when correct data from device tree is available
- Using therma_cdev_update() when thermal is ready for controlling the fan
Changes for v3:
- Rename patch heading
- pwm_fan_of_get_cooling_data() now returns -EINVAL when no "cooling-levels"
property defined
- register of cooling device only when proper cooling data is present
Changes for v4:
- None
Changes for v5:
- Check for IS_ENABLED(CONFIG_THERMAL) has been added to prevent from
executing thermal_* specific functions
Changes for v6:
- Adding missing ctx == NULL check in pwm_fan_get_max_state()
- Call to thermal_cooling_device_unregister(ctx->cdev); at pwm_fan_remove()
- struct thermal_cooling_device *cdev; added to struct pwm_fan_ctx
---
drivers/hwmon/pwm-fan.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 88 insertions(+), 1 deletion(-)
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index e6ed353..7c83dc4 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -24,6 +24,7 @@
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/sysfs.h>
+#include <linux/thermal.h>
#define MAX_PWM 255
@@ -34,6 +35,7 @@ struct pwm_fan_ctx {
unsigned int pwm_fan_state;
unsigned int pwm_fan_max_state;
unsigned int *pwm_fan_cooling_levels;
+ struct thermal_cooling_device *cdev;
};
static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
@@ -68,6 +70,17 @@ exit_set_pwm_err:
return ret;
}
+static void pwm_fan_update_state(struct pwm_fan_ctx *ctx, unsigned long pwm)
+{
+ int i;
+
+ for (i = 0; i < ctx->pwm_fan_max_state; ++i)
+ if (pwm < ctx->pwm_fan_cooling_levels[i + 1])
+ break;
+
+ ctx->pwm_fan_state = i;
+}
+
static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -82,6 +95,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
if (ret)
return ret;
+ pwm_fan_update_state(ctx, pwm);
return count;
}
@@ -103,6 +117,62 @@ static struct attribute *pwm_fan_attrs[] = {
ATTRIBUTE_GROUPS(pwm_fan);
+/* thermal cooling device callbacks */
+static int pwm_fan_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+
+ if (!ctx)
+ return -EINVAL;
+
+ *state = ctx->pwm_fan_max_state;
+
+ return 0;
+}
+
+static int pwm_fan_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+
+ if (!ctx)
+ return -EINVAL;
+
+ *state = ctx->pwm_fan_state;
+
+ return 0;
+}
+
+static int
+pwm_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+ int ret;
+
+ if (!ctx || (state > ctx->pwm_fan_max_state))
+ return -EINVAL;
+
+ if (state == ctx->pwm_fan_state)
+ return 0;
+
+ ret = __set_pwm(ctx, ctx->pwm_fan_cooling_levels[state]);
+ if (ret) {
+ dev_err(&cdev->device, "Cannot set pwm!\n");
+ return ret;
+ }
+
+ ctx->pwm_fan_state = state;
+
+ return ret;
+}
+
+static const struct thermal_cooling_device_ops pwm_fan_cooling_ops = {
+ .get_max_state = pwm_fan_get_max_state,
+ .get_cur_state = pwm_fan_get_cur_state,
+ .set_cur_state = pwm_fan_set_cur_state,
+};
+
int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
{
struct device_node *np = dev->of_node;
@@ -145,8 +215,9 @@ int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx)
static int pwm_fan_probe(struct platform_device *pdev)
{
- struct device *hwmon;
+ struct thermal_cooling_device *cdev;
struct pwm_fan_ctx *ctx;
+ struct device *hwmon;
int duty_cycle;
int ret;
@@ -193,6 +264,21 @@ static int pwm_fan_probe(struct platform_device *pdev)
if (ret)
return ret;
+ ctx->pwm_fan_state = ctx->pwm_fan_max_state;
+ if (IS_ENABLED(CONFIG_THERMAL)) {
+ cdev = thermal_of_cooling_device_register(pdev->dev.of_node,
+ "pwm-fan", ctx,
+ &pwm_fan_cooling_ops);
+ if (IS_ERR(cdev)) {
+ dev_err(&pdev->dev,
+ "Failed to register pwm-fan as cooling device");
+ pwm_disable(ctx->pwm);
+ return PTR_ERR(cdev);
+ }
+ ctx->cdev = cdev;
+ thermal_cdev_update(cdev);
+ }
+
return 0;
}
@@ -200,6 +286,7 @@ static int pwm_fan_remove(struct platform_device *pdev)
{
struct pwm_fan_ctx *ctx = platform_get_drvdata(pdev);
+ thermal_cooling_device_unregister(ctx->cdev);
if (ctx->pwm_value)
pwm_disable(ctx->pwm);
return 0;
--
2.0.0.rc2
On 02/26/2015 05:59 AM, Lukasz Majewski wrote:
> The PWM FAN device can now be used as a thermal cooling device. Necessary
> infrastructure has been added in this commit.
>
> Signed-off-by: Lukasz Majewski <[email protected]>
> Acked-by: Eduardo Valentin <[email protected]>
> ---
> Changes for v2:
> - Replace pwm_fan_cooling_states with pwm_fan_cooling_levels
> - Update ctx->pwm_fan_state when correct data from device tree is available
> - Using therma_cdev_update() when thermal is ready for controlling the fan
> Changes for v3:
> - Rename patch heading
> - pwm_fan_of_get_cooling_data() now returns -EINVAL when no "cooling-levels"
> property defined
> - register of cooling device only when proper cooling data is present
> Changes for v4:
> - None
> Changes for v5:
> - Check for IS_ENABLED(CONFIG_THERMAL) has been added to prevent from
> executing thermal_* specific functions
> Changes for v6:
> - Adding missing ctx == NULL check in pwm_fan_get_max_state()
> - Call to thermal_cooling_device_unregister(ctx->cdev); at pwm_fan_remove()
> - struct thermal_cooling_device *cdev; added to struct pwm_fan_ctx
> ---
> drivers/hwmon/pwm-fan.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 88 insertions(+), 1 deletion(-)
[ ... ]
>
> @@ -200,6 +286,7 @@ static int pwm_fan_remove(struct platform_device *pdev)
> {
> struct pwm_fan_ctx *ctx = platform_get_drvdata(pdev);
>
> + thermal_cooling_device_unregister(ctx->cdev);
Unfortunately there is still no prototype for this if CONFIG_THERMAL
is not configured.
Two options: Yet another revision, or wait a week until the prototypes
are (hopefully) available and submit a patch without the conditionals.
Guenter
Hi Guenter,
> On 02/26/2015 05:59 AM, Lukasz Majewski wrote:
> > The PWM FAN device can now be used as a thermal cooling device.
> > Necessary infrastructure has been added in this commit.
> >
> > Signed-off-by: Lukasz Majewski <[email protected]>
> > Acked-by: Eduardo Valentin <[email protected]>
> > ---
> > Changes for v2:
> > - Replace pwm_fan_cooling_states with pwm_fan_cooling_levels
> > - Update ctx->pwm_fan_state when correct data from device tree is
> > available
> > - Using therma_cdev_update() when thermal is ready for controlling
> > the fan Changes for v3:
> > - Rename patch heading
> > - pwm_fan_of_get_cooling_data() now returns -EINVAL when no
> > "cooling-levels" property defined
> > - register of cooling device only when proper cooling data is
> > present Changes for v4:
> > - None
> > Changes for v5:
> > - Check for IS_ENABLED(CONFIG_THERMAL) has been added to prevent
> > from executing thermal_* specific functions
> > Changes for v6:
> > - Adding missing ctx == NULL check in pwm_fan_get_max_state()
> > - Call to thermal_cooling_device_unregister(ctx->cdev); at
> > pwm_fan_remove()
> > - struct thermal_cooling_device *cdev; added to struct pwm_fan_ctx
> > ---
> > drivers/hwmon/pwm-fan.c | 89
> > ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed,
> > 88 insertions(+), 1 deletion(-)
> [ ... ]
> >
> > @@ -200,6 +286,7 @@ static int pwm_fan_remove(struct
> > platform_device *pdev) {
> > struct pwm_fan_ctx *ctx = platform_get_drvdata(pdev);
> >
> > + thermal_cooling_device_unregister(ctx->cdev);
>
> Unfortunately there is still no prototype for this if CONFIG_THERMAL
> is not configured.
Yes... since Nishanth Menon's changes are now only in
linux-soc-thermal/fixes
>
> Two options: Yet another revision, or wait a week until the prototypes
> are (hopefully) available and submit a patch without the conditionals.
No problem, I will wait a bit and resend this patch set.
>
> Guenter
>
--
Best regards,
Lukasz Majewski
Samsung R&D Institute Poland (SRPOL) | Linux Platform Group