|
@@ -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,18 @@
|
|
|
/* How long we sleep in us while waiting for an RPM result. */
|
|
|
#define ASPEED_RPM_STATUS_SLEEP_USEC 500
|
|
|
|
|
|
+#define MAX_CDEV_NAME_LEN 16
|
|
|
+
|
|
|
+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 +193,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];
|
|
|
};
|
|
|
|
|
@@ -765,6 +779,94 @@ static void aspeed_create_fan_tach_channel(struct aspeed_pwm_tacho_data *priv,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static int
|
|
|
+aspeed_pwm_cz_get_max_state(struct thermal_cooling_device *tcdev,
|
|
|
+ unsigned long *state)
|
|
|
+{
|
|
|
+ struct aspeed_cooling_device *cdev = 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 = 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 = 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, u8 num_levels)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+ struct aspeed_cooling_device *cdev;
|
|
|
+
|
|
|
+ cdev = devm_kzalloc(dev, sizeof(*cdev), GFP_KERNEL);
|
|
|
+
|
|
|
+ if (!cdev)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ cdev->cooling_levels = devm_kzalloc(dev, num_levels, GFP_KERNEL);
|
|
|
+ if (!cdev->cooling_levels)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ cdev->max_state = num_levels - 1;
|
|
|
+ ret = of_property_read_u8_array(child, "cooling-levels",
|
|
|
+ cdev->cooling_levels,
|
|
|
+ num_levels);
|
|
|
+ if (ret) {
|
|
|
+ dev_err(dev, "Property 'cooling-levels' cannot be read.\n");
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ snprintf(cdev->name, MAX_CDEV_NAME_LEN, "%s%d", child->name, pwm_port);
|
|
|
+
|
|
|
+ cdev->tcdev = thermal_of_cooling_device_register(child,
|
|
|
+ cdev->name,
|
|
|
+ cdev,
|
|
|
+ &aspeed_pwm_cool_ops);
|
|
|
+ if (IS_ERR(cdev->tcdev))
|
|
|
+ return PTR_ERR(cdev->tcdev);
|
|
|
+
|
|
|
+ cdev->priv = priv;
|
|
|
+ cdev->pwm_port = pwm_port;
|
|
|
+
|
|
|
+ priv->cdev[pwm_port] = cdev;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int aspeed_create_fan(struct device *dev,
|
|
|
struct device_node *child,
|
|
|
struct aspeed_pwm_tacho_data *priv)
|
|
@@ -778,6 +880,15 @@ static int aspeed_create_fan(struct device *dev,
|
|
|
return ret;
|
|
|
aspeed_create_pwm_port(priv, (u8)pwm_port);
|
|
|
|
|
|
+ ret = of_property_count_u8_elems(child, "cooling-levels");
|
|
|
+
|
|
|
+ if (ret > 0) {
|
|
|
+ ret = aspeed_create_pwm_cooling(dev, child, priv, pwm_port,
|
|
|
+ ret);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
count = of_property_count_u8_elems(child, "aspeed,fan-tach-ch");
|
|
|
if (count < 1)
|
|
|
return -EINVAL;
|
|
@@ -834,9 +945,10 @@ static int aspeed_pwm_tacho_probe(struct platform_device *pdev)
|
|
|
|
|
|
for_each_child_of_node(np, child) {
|
|
|
ret = aspeed_create_fan(dev, child, priv);
|
|
|
- of_node_put(child);
|
|
|
- if (ret)
|
|
|
+ if (ret) {
|
|
|
+ of_node_put(child);
|
|
|
return ret;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
priv->groups[0] = &pwm_dev_group;
|