[PATCH linux dev-4.10 v2] hwmon: (aspeed-pwm-tacho) cooling device support
Mykola Kostenok
c_mykolak at mellanox.com
Fri Jul 7 00:38:42 AEST 2017
Add support in aspeed-pwm-tacho driver for cooling device creation.
This cooling device could be bound to a thermal zone
for the thermal control. Device will appear in /sys/class/thermal
folder as cooling_deviceX. Then it could be bound to particular
thermal zones. Allow specification of the cooling levels
vector - PWM duty cycle values in a range from 0 to 255
which correspond to thermal cooling states.
Add basic pwm-tacho-controller node to ast-g5 dtsi.
Add basic pwm-tacho-controller node to ast-g4 dtsi.
Change pwm-tacho in aspeed-bmc-quanta-q71l.dts according new
pwm-tacho structure.
Add pwm-tacho to aspeed-bmc-mellanox-msn.dts.
v1->v2
Fix issues pointet out by Patrick:
- rename aspeed_create_pwm to aspeed_create_pwm_cooling.
Signed-off-by: Mykola Kostenok <c_mykolak at mellanox.com>
---
arch/arm/boot/dts/aspeed-bmc-mellanox-msn.dts | 61 +++++++++++
arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts | 103 +++++++++----------
arch/arm/boot/dts/aspeed-g4.dtsi | 9 +-
arch/arm/boot/dts/aspeed-g5.dtsi | 17 ++++
drivers/hwmon/aspeed-pwm-tacho.c | 139 +++++++++++++++++++++++++-
5 files changed, 273 insertions(+), 56 deletions(-)
diff --git a/arch/arm/boot/dts/aspeed-bmc-mellanox-msn.dts b/arch/arm/boot/dts/aspeed-bmc-mellanox-msn.dts
index c71a6dbe1410..57d595124dea 100644
--- a/arch/arm/boot/dts/aspeed-bmc-mellanox-msn.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-mellanox-msn.dts
@@ -144,3 +144,64 @@
status = "okay";
};
+&pwm_tacho {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_pwm0_default>;
+
+ tach-channels {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ fan at 0 {
+ reg = <0x00>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x00>;
+ };
+
+ fan at 1 {
+ reg = <0x00>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x01>;
+ };
+
+ fan at 2 {
+ reg = <0x00>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x02>;
+ };
+
+ fan at 3 {
+ reg = <0x00>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x03>;
+ };
+
+ fan at 4 {
+ reg = <0x00>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x04>;
+ };
+
+ fan at 5 {
+ reg = <0x00>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x05>;
+ };
+
+ fan at 6 {
+ reg = <0x00>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x06>;
+ };
+
+ fan at 7 {
+ reg = <0x00>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x07>;
+ };
+ };
+
+ pwm-channels {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #cooling-cells = <2>;
+
+ pwm at 0 {
+ reg = <0x00>;
+ cooling-levels = /bits/ 8 <125 151 177 203 229 255>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts b/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts
index bb287fb46bb1..43ada5baea02 100644
--- a/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts
@@ -46,57 +46,6 @@
gpios = <&gpio ASPEED_GPIO(B, 3) GPIO_ACTIVE_LOW>;
};
};
-
- pwm_tacho: pwm-tacho-controller at 1e786000 {
- compatible = "aspeed,ast2500-pwm-tacho";
- #address-cells = <1>;
- #size-cells = <0>;
- reg = <0x1e786000 0x1000>;
- clocks = <&pwm_tacho_fixed_clk>;
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_pwm0_default &pinctrl_pwm1_default
- &pinctrl_pwm2_default &pinctrl_pwm3_default>;
-
- fan at 0 {
- reg = <0x00>;
- aspeed,fan-tach-ch = /bits/ 8 <0x00>;
- };
-
- fan at 1 {
- reg = <0x01>;
- aspeed,fan-tach-ch = /bits/ 8 <0x01>;
- };
-
- fan at 2 {
- reg = <0x02>;
- aspeed,fan-tach-ch = /bits/ 8 <0x02>;
- };
-
- fan at 3 {
- reg = <0x03>;
- aspeed,fan-tach-ch = /bits/ 8 <0x03>;
- };
-
- fan at 4 {
- reg = <0x00>;
- aspeed,fan-tach-ch = /bits/ 8 <0x04>;
- };
-
- fan at 5 {
- reg = <0x01>;
- aspeed,fan-tach-ch = /bits/ 8 <0x05>;
- };
-
- fan at 6 {
- reg = <0x02>;
- aspeed,fan-tach-ch = /bits/ 8 <0x06>;
- };
-
- fan at 7 {
- reg = <0x03>;
- aspeed,fan-tach-ch = /bits/ 8 <0x07>;
- };
- };
};
&fmc {
@@ -299,3 +248,55 @@
&wdt2 {
status = "okay";
};
+
+&pwm_tacho {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_pwm0_default &pinctrl_pwm1_default
+ &pinctrl_pwm2_default &pinctrl_pwm3_default>;
+
+ tach-channels {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ fan at 0 {
+ reg = <0x00>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x00>;
+ };
+
+ fan at 1 {
+ reg = <0x01>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x01>;
+ };
+
+ fan at 2 {
+ reg = <0x02>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x02>;
+ };
+
+ fan at 3 {
+ reg = <0x03>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x03>;
+ };
+
+ fan at 4 {
+ reg = <0x00>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x04>;
+ };
+
+ fan at 5 {
+ reg = <0x01>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x05>;
+ };
+
+ fan at 6 {
+ reg = <0x02>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x06>;
+ };
+
+ fan at 7 {
+ reg = <0x03>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x07>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
index 9cc959fccfa0..c69ad7744921 100644
--- a/arch/arm/boot/dts/aspeed-g4.dtsi
+++ b/arch/arm/boot/dts/aspeed-g4.dtsi
@@ -318,7 +318,14 @@
ranges = <0 0x1e78a000 0x1000>;
};
-
+ pwm_tacho: pwm-tacho-controller at 1e786000 {
+ compatible = "aspeed,ast2500-pwm-tacho";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x1e786000 0x1000>;
+ clocks = <&pwm_tacho_fixed_clk>;
+ status = "disabled";
+ };
};
};
};
diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
index 992242d3b22e..1ed29d9fa959 100644
--- a/arch/arm/boot/dts/aspeed-g5.dtsi
+++ b/arch/arm/boot/dts/aspeed-g5.dtsi
@@ -35,6 +35,14 @@
i2c13 = &i2c13;
};
+ clocks {
+ pwm_tacho_fixed_clk: fixedclk {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <24000000>;
+ };
+ };
+
ahb {
compatible = "simple-bus";
#address-cells = <1>;
@@ -366,6 +374,15 @@
#size-cells = <1>;
ranges = <0 0x1e78a000 0x1000>;
};
+
+ pwm_tacho: pwm-tacho-controller at 1e786000 {
+ compatible = "aspeed,ast2500-pwm-tacho";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x1e786000 0x1000>;
+ clocks = <&pwm_tacho_fixed_clk>;
+ status = "disabled";
+ };
};
};
};
diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c
index ddfe66bdff86..c9aafed83f4e 100644
--- a/drivers/hwmon/aspeed-pwm-tacho.c
+++ b/drivers/hwmon/aspeed-pwm-tacho.c
@@ -20,6 +20,7 @@
#include <linux/platform_device.h>
#include <linux/sysfs.h>
#include <linux/regmap.h>
+#include <linux/thermal.h>
/* ASPEED PWM & FAN Tach Register Definition */
#define ASPEED_PTCR_CTRL 0x00
@@ -166,6 +167,16 @@
/* How long we sleep in us while waiting for an RPM result. */
#define ASPEED_RPM_STATUS_SLEEP_USEC 500
+struct aspeed_cooling_device {
+ char name[16];
+ struct aspeed_pwm_tacho_data *priv;
+ struct thermal_cooling_device *tcdev;
+ int pwm_port;
+ u8 *cooling_levels;
+ u8 max_state;
+ u8 cur_state;
+};
+
struct aspeed_pwm_tacho_data {
struct regmap *regmap;
unsigned long clk_freq;
@@ -180,6 +191,7 @@ struct aspeed_pwm_tacho_data {
u8 pwm_port_type[8];
u8 pwm_port_fan_ctrl[8];
u8 fan_tach_ch_source[16];
+ struct aspeed_cooling_device *cdev[8];
const struct attribute_group *groups[3];
};
@@ -794,10 +806,111 @@ static int aspeed_create_fan(struct device *dev,
return 0;
}
+static int
+aspeed_pwm_cz_get_max_state(struct thermal_cooling_device *tcdev,
+ unsigned long *state)
+{
+ struct aspeed_cooling_device *cdev =
+ (struct aspeed_cooling_device *)tcdev->devdata;
+
+ *state = cdev->max_state;
+
+ return 0;
+}
+
+static int
+aspeed_pwm_cz_get_cur_state(struct thermal_cooling_device *tcdev,
+ unsigned long *state)
+{
+ struct aspeed_cooling_device *cdev =
+ (struct aspeed_cooling_device *)tcdev->devdata;
+
+ *state = cdev->cur_state;
+
+ return 0;
+}
+
+static int
+aspeed_pwm_cz_set_cur_state(struct thermal_cooling_device *tcdev,
+ unsigned long state)
+{
+ struct aspeed_cooling_device *cdev =
+ (struct aspeed_cooling_device *)tcdev->devdata;
+
+ if (state > cdev->max_state)
+ return -EINVAL;
+
+ cdev->cur_state = state;
+ cdev->priv->pwm_port_fan_ctrl[cdev->pwm_port] = *(cdev->cooling_levels
+ + cdev->cur_state);
+ aspeed_set_pwm_port_fan_ctrl(cdev->priv, cdev->pwm_port,
+ *(cdev->cooling_levels +
+ cdev->cur_state));
+
+ return 0;
+}
+
+static const struct thermal_cooling_device_ops aspeed_pwm_cool_ops = {
+ .get_max_state = aspeed_pwm_cz_get_max_state,
+ .get_cur_state = aspeed_pwm_cz_get_cur_state,
+ .set_cur_state = aspeed_pwm_cz_set_cur_state,
+};
+
+static int aspeed_create_pwm_cooling(struct device *dev,
+ struct device_node *child,
+ struct aspeed_pwm_tacho_data *priv)
+{
+ u32 pwm_port;
+ int ret;
+
+ ret = of_property_read_u32(child, "reg", &pwm_port);
+ if (ret)
+ return ret;
+
+ ret = of_property_count_u8_elems(child, "cooling-levels");
+ if (ret <= 0) {
+ dev_err(dev, "Wrong cooling-levels data.\n");
+ return -EINVAL;
+ }
+
+ priv->cdev[pwm_port] = devm_kzalloc(dev, sizeof(*priv->cdev[pwm_port]),
+ GFP_KERNEL);
+ if (!priv->cdev[pwm_port])
+ return -ENOMEM;
+
+ priv->cdev[pwm_port]->cooling_levels = devm_kzalloc(dev, ret *
+ sizeof(u8),
+ GFP_KERNEL);
+ if (!priv->cdev[pwm_port]->cooling_levels)
+ return -ENOMEM;
+
+ priv->cdev[pwm_port]->max_state = ret - 1;
+ ret = of_property_read_u8_array(child, "cooling-levels",
+ priv->cdev[pwm_port]->cooling_levels,
+ ret);
+ if (ret) {
+ dev_err(dev, "Property 'cooling-levels' cannot be read.\n");
+ return ret;
+ }
+
+ sprintf(priv->cdev[pwm_port]->name, "%s%d", child->name, pwm_port);
+ priv->cdev[pwm_port]->tcdev = thermal_of_cooling_device_register(child,
+ priv->cdev[pwm_port]->name,
+ priv->cdev[pwm_port],
+ &aspeed_pwm_cool_ops);
+ if (PTR_ERR_OR_ZERO(priv->cdev[pwm_port]->tcdev))
+ return PTR_ERR(priv->cdev[pwm_port]->tcdev);
+
+ priv->cdev[pwm_port]->priv = priv;
+ priv->cdev[pwm_port]->pwm_port = pwm_port;
+
+ return 0;
+}
+
static int aspeed_pwm_tacho_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct device_node *np, *child;
+ struct device_node *np, *child, *grandchild;
struct aspeed_pwm_tacho_data *priv;
void __iomem *regs;
struct resource *res;
@@ -833,10 +946,28 @@ static int aspeed_pwm_tacho_probe(struct platform_device *pdev)
aspeed_create_type(priv);
for_each_child_of_node(np, child) {
- ret = aspeed_create_fan(dev, child, priv);
+ if (!of_node_cmp(child->name, "tach-channels")) {
+ for_each_child_of_node(child, grandchild) {
+ ret = aspeed_create_fan(dev, grandchild, priv);
+ of_node_put(grandchild);
+ if (ret) {
+ of_node_put(child);
+ return ret;
+ }
+ }
+ }
+
+ if (!of_node_cmp(child->name, "pwm-channels")) {
+ for_each_child_of_node(child, grandchild) {
+ ret = aspeed_create_pwm_cooling(dev, grandchild, priv);
+ of_node_put(grandchild);
+ if (ret) {
+ of_node_put(child);
+ return ret;
+ }
+ }
+ }
of_node_put(child);
- if (ret)
- return ret;
}
priv->groups[0] = &pwm_dev_group;
--
2.11.0
More information about the openbmc
mailing list