1
0
Fork 0

hwmon: (aspeed-pwm-tacho) cooling device support.

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.

Signed-off-by: Mykola Kostenok <c_mykolak@mellanox.com>
Reviewed-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
zero-colors
Mykola Kostenok 2017-08-03 11:50:44 +03:00 committed by Guenter Roeck
parent 5e047541c1
commit f198907d2f
1 changed files with 114 additions and 2 deletions

View File

@ -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;