Hi, On Fri, Apr 19, 2019 at 11:02:33PM -0400, Jean-Francois Dagenais wrote: > This allows max6650 devices to be referenced in dts as a cooling device. > > The pwm value seems duplicated in cooling_dev_state but since pwm goes > through rounding logic into data->dac, it is modified and messes with > the thermal zone state algorithms. It's also better to serve a cache > value, thus avoiding periodic actual i2c traffic. > > Signed-off-by: Jean-Francois Dagenais <jeff.dagenais@xxxxxxxxx> I finally had some time to play with the driver and patch, and I do have a concern after all. Please see below. > --- > Changes in v2: > - Removed left-over debug printk. > Changes in v3: > - Add missing dependency in Kconfig > Changes in v4: > - Remove useless comment "thermal cooling device callbacks" > - fix max6650_set_cur_state signature declaration style > - Only warn when thermal_of_cooling_device_register fails > Changes in v5: > - Use switch to non-const client->name to prevent compiler warning > - fix incorrect length of printk format long int > > drivers/hwmon/Kconfig | 1 + > drivers/hwmon/max6650.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++- > 2 files changed, 91 insertions(+), 1 deletion(-) > > diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig > index d0f1dfe2bcbb..46d69fcdd48b 100644 > --- a/drivers/hwmon/Kconfig > +++ b/drivers/hwmon/Kconfig > @@ -898,6 +898,7 @@ config SENSORS_MAX6642 > config SENSORS_MAX6650 > tristate "Maxim MAX6650 sensor chip" > depends on I2C > + depends on THERMAL || THERMAL=n > help > If you say yes here you get support for the MAX6650 / MAX6651 > sensor chips. > diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c > index 61135a2d0cff..0607d54f8090 100644 > --- a/drivers/hwmon/max6650.c > +++ b/drivers/hwmon/max6650.c > @@ -40,6 +40,7 @@ > #include <linux/hwmon-sysfs.h> > #include <linux/err.h> > #include <linux/of_device.h> > +#include <linux/thermal.h> > > /* > * Insmod parameters > @@ -113,6 +114,7 @@ module_param(clock, int, 0444); > struct max6650_data { > struct i2c_client *client; > const struct attribute_group *groups[3]; > + struct thermal_cooling_device *cooling_dev; > struct mutex update_lock; > int nr_fans; > char valid; /* zero until following fields are valid */ > @@ -125,6 +127,7 @@ struct max6650_data { > u8 count; > u8 dac; > u8 alarm; > + unsigned long cooling_dev_state; > }; > > static const u8 tach_reg[] = { > @@ -694,6 +697,63 @@ static int max6650_init_client(struct max6650_data *data, > return 0; > } > > +#if IS_ENABLED(CONFIG_THERMAL) > + > +static int max6650_get_max_state(struct thermal_cooling_device *cdev, > + unsigned long *state) > +{ > + *state = 255; > + > + return 0; > +} > + > +static int max6650_get_cur_state(struct thermal_cooling_device *cdev, > + unsigned long *state) > +{ > + struct max6650_data *data = cdev->devdata; > + > + *state = data->cooling_dev_state; > + > + return 0; > +} > + > +static int max6650_set_cur_state(struct thermal_cooling_device *cdev, > + unsigned long state) > +{ > + struct max6650_data *data = cdev->devdata; > + struct i2c_client *client = data->client; > + int err; > + > + state = clamp_val(state, 0, 255); > + > + mutex_lock(&data->update_lock); > + > + if (data->config & MAX6650_CFG_V12) > + data->dac = 180 - (180 * state)/255; > + else > + data->dac = 76 - (76 * state)/255; > + > + err = i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, data->dac); > + > + if (!err) { > + max6650_set_operating_mode(data, state ? > + MAX6650_CFG_MODE_OPEN_LOOP : > + MAX6650_CFG_MODE_OFF); > + data->cooling_dev_state = state; > + } > + > + mutex_unlock(&data->update_lock); > + > + return err < 0 ? err : 0; > +} > + > +static const struct thermal_cooling_device_ops max6650_cooling_ops = { > + .get_max_state = max6650_get_max_state, > + .get_cur_state = max6650_get_cur_state, > + .set_cur_state = max6650_set_cur_state, > +}; > +#endif > + > static int max6650_probe(struct i2c_client *client, > const struct i2c_device_id *id) > { > @@ -709,6 +769,7 @@ static int max6650_probe(struct i2c_client *client, > return -ENOMEM; > > data->client = client; > + i2c_set_clientdata(client, data); > mutex_init(&data->update_lock); > data->nr_fans = of_id ? (int)(uintptr_t)of_id->data : id->driver_data; > > @@ -727,7 +788,34 @@ static int max6650_probe(struct i2c_client *client, > hwmon_dev = devm_hwmon_device_register_with_groups(dev, > client->name, data, > data->groups); > - return PTR_ERR_OR_ZERO(hwmon_dev); > + err = PTR_ERR_OR_ZERO(hwmon_dev); > + if (err) > + return err; > + > + > +#if IS_ENABLED(CONFIG_THERMAL) > + data->cooling_dev = > + thermal_of_cooling_device_register(client->dev.of_node, > + client->name, data, > + &max6650_cooling_ops); > + if (IS_ERR(data->cooling_dev)) > + dev_warn(&client->dev, > + "thermal cooling device register failed: %ld\n", > + PTR_ERR(data->cooling_dev)); > + else > + thermal_cdev_update(data->cooling_dev); On a system which has CONFIG_THERMAL enabled but does not really utilize it, the call to thermal_cdev_update() will result in the fan being stopped immediately since max6650_set_cur_state() is called with state=0. I see that this function called from pwm_fan.c. It is not called from gpio-fan.c or npcm750-pwm-fan.c. Other than from pwm-fan.c, it is only called from thermal governors, and not from any other thermal drivers registering a thermal cooling device. This leads me to believe that the calls from here and from pwm_fan.c may be wrong. Are you sure that this call needed ? If so, can you explain ? What happens in your system if you remove this call ? Thanks, Guenter > +#endif > + return 0; > +} > + > +static int max6650_remove(struct i2c_client *client) > +{ > + struct max6650_data *data = i2c_get_clientdata(client); > + > + if (!IS_ERR(data->cooling_dev)) > + thermal_cooling_device_unregister(data->cooling_dev); > + > + return 0; > } > > static const struct i2c_device_id max6650_id[] = { > @@ -743,6 +831,7 @@ static struct i2c_driver max6650_driver = { > .of_match_table = of_match_ptr(max6650_dt_match), > }, > .probe = max6650_probe, > + .remove = max6650_remove, > .id_table = max6650_id, > }; >