Message ID | 20190420030233.4651-1-jeff.dagenais@gmail.com (mailing list archive) |
---|---|
State | Accepted |
Headers | show |
Series | [v5] hwmon: max6650: add thermal cooling device capability | expand |
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@gmail.com> 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, > }; >
> On Apr 22, 2019, at 09:40, Guenter Roeck <linux@roeck-us.net> wrote: > > 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 ? Yes, you are correct. It is a carry over from pwm-fan.c and so probably wrong there too. I have tested without the thermal_cdev_update call in probe() and basically, if the cooling device is not part of a TZ, then set_cur_state is never called. If part of a TZ, it is called by the governor (step_wise in my case) as required and all works as expected. Cheers!
On 4/23/19 5:45 AM, Jean-Francois Dagenais wrote: > >> On Apr 22, 2019, at 09:40, Guenter Roeck <linux@roeck-us.net> wrote: >> >> 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 ? > > Yes, you are correct. It is a carry over from pwm-fan.c and so probably wrong > there too. I have tested without the thermal_cdev_update call in probe() and > basically, if the cooling device is not part of a TZ, then set_cur_state is > never called. If part of a TZ, it is called by the governor (step_wise in my > case) as required and all works as expected. > Excellent - thanks for testing and getting back. Guenter
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); +#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, };
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@gmail.com> --- 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(-)