From 0ff1a316c0f0ca36c49f2415f41d49ef1581808d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 20 May 2022 21:54:48 -0700 Subject: [PATCH] hwmon: Implement hwmon_update_groups() In some situations the visibility of hwmon sysfs attributes may change. Support this by providing a new API function hwmon_update_groups(). Signed-off-by: Guenter Roeck --- drivers/hwmon/hwmon.c | 95 +++++++++++++++++++++++++++++++++---------- include/linux/hwmon.h | 2 + 2 files changed, 76 insertions(+), 21 deletions(-) diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 2e2cd79d89eb..d842ab607ccf 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -248,7 +248,19 @@ static int hwmon_thermal_add_sensor(struct device *dev, int index) return 0; } -static int hwmon_thermal_register_sensors(struct device *dev) +static struct hwmon_thermal_data *hwmon_thermal_find_tz(struct device *dev, int index) +{ + struct hwmon_device *hwdev = to_hwmon_device(dev); + struct hwmon_thermal_data *tzdata; + + list_for_each_entry(tzdata, &hwdev->tzdata, node) { + if (tzdata->index == index) + return tzdata; + } + return NULL; +} + +static int hwmon_thermal_register_sensors(struct device *dev, bool update) { struct hwmon_device *hwdev = to_hwmon_device(dev); const struct hwmon_chip_info *chip = hwdev->chip; @@ -263,16 +275,30 @@ static int hwmon_thermal_register_sensors(struct device *dev) continue; for (j = 0; info[i]->config[j]; j++) { + umode_t mode; int err; - if (!(info[i]->config[j] & HWMON_T_INPUT) || - !chip->ops->is_visible(drvdata, hwmon_temp, - hwmon_temp_input, j)) + if (!(info[i]->config[j] & HWMON_T_INPUT)) continue; - - err = hwmon_thermal_add_sensor(dev, j); - if (err) - return err; + mode = chip->ops->is_visible(drvdata, hwmon_temp, + hwmon_temp_input, j); + if (!mode) { + struct hwmon_thermal_data *tzdata; + + if (!update) + continue; + tzdata = hwmon_thermal_find_tz(dev, j); + if (tzdata) { + devm_thermal_zone_of_sensor_unregister(dev, tzdata->tzd); + devm_release_action(dev, hwmon_thermal_remove_sensor, &tzdata->node); + } + } else { + if (!update || !hwmon_thermal_find_tz(dev, j)) { + err = hwmon_thermal_add_sensor(dev, j); + if (err) + return err; + } + } } } @@ -281,15 +307,11 @@ static int hwmon_thermal_register_sensors(struct device *dev) static void hwmon_thermal_notify(struct device *dev, int index) { - struct hwmon_device *hwdev = to_hwmon_device(dev); - struct hwmon_thermal_data *tzdata; + struct hwmon_thermal_data *tzdata = hwmon_thermal_find_tz(dev, index); - list_for_each_entry(tzdata, &hwdev->tzdata, node) { - if (tzdata->index == index) { - thermal_zone_device_update(tzdata->tzd, - THERMAL_EVENT_UNSPECIFIED); - } - } + if (tzdata) + thermal_zone_device_update(tzdata->tzd, + THERMAL_EVENT_UNSPECIFIED); } #else @@ -401,10 +423,12 @@ static struct attribute *hwmon_genattr(const void *drvdata, if (!template) return ERR_PTR(-ENOENT); + /* + * Basic mode sanity check. This is less than perfect since + * attribute visibility and with it the mode can change during + * runtime, but it is the best we can do. + */ mode = ops->is_visible(drvdata, type, attr, index); - if (!mode) - return ERR_PTR(-ENOENT); - if ((mode & 0444) && ((is_string && !ops->read_string) || (!is_string && !ops->read))) return ERR_PTR(-EINVAL); @@ -435,7 +459,7 @@ static struct attribute *hwmon_genattr(const void *drvdata, a = &dattr->attr; sysfs_attr_init(a); a->name = name; - a->mode = mode; + a->mode = ops->write ? 0644 : 0444; /* updated when attributes are generated */ return a; } @@ -638,6 +662,24 @@ static const int __templates_size[] = { [hwmon_intrusion] = ARRAY_SIZE(hwmon_intrusion_attr_templates), }; +int hwmon_update_groups(struct device *dev) +{ + struct hwmon_device *hwdev = to_hwmon_device(dev); + const struct hwmon_chip_info *chip = hwdev->chip; + const struct hwmon_channel_info **info = chip->info; + int ret; + + ret = sysfs_update_groups(&dev->kobj, dev->groups); + if (ret) + return ret; + + if (info[0]->type != hwmon_chip || !(info[0]->config[0] & HWMON_C_REGISTER_TZ)) + return 0; + + return hwmon_thermal_register_sensors(dev, true); +} +EXPORT_SYMBOL_GPL(hwmon_update_groups); + int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel) { @@ -748,6 +790,16 @@ __hwmon_create_attrs(const void *drvdata, const struct hwmon_chip_info *chip) return attrs; } +static umode_t hwmon_is_visible(struct kobject *kobj, struct attribute *attr, int n) +{ + struct device_attribute *dattr = to_dev_attr(attr); + struct hwmon_device_attribute *hattr = to_hwmon_attr(dattr); + struct device *dev = kobj_to_dev(kobj); + void *drvdata = dev_get_drvdata(dev); + + return hattr->ops->is_visible(drvdata, hattr->type, hattr->attr, hattr->index); +} + static struct device * __hwmon_device_register(struct device *dev, const char *name, void *drvdata, const struct hwmon_chip_info *chip, @@ -797,6 +849,7 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata, } hwdev->group.attrs = attrs; + hwdev->group.is_visible = hwmon_is_visible; ngroups = 0; hwdev->groups[ngroups++] = &hwdev->group; @@ -840,7 +893,7 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata, if (dev && dev->of_node && chip && chip->ops->read && chip->info[0]->type == hwmon_chip && (chip->info[0]->config[0] & HWMON_C_REGISTER_TZ)) { - err = hwmon_thermal_register_sensors(hdev); + err = hwmon_thermal_register_sensors(hdev, false); if (err) { device_unregister(hdev); /* diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h index 14325f93c6b2..389ed45d4bd7 100644 --- a/include/linux/hwmon.h +++ b/include/linux/hwmon.h @@ -467,6 +467,8 @@ int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type, char *hwmon_sanitize_name(const char *name); char *devm_hwmon_sanitize_name(struct device *dev, const char *name); +int hwmon_update_groups(struct device *dev); + /** * hwmon_is_bad_char - Is the char invalid in a hwmon name * @ch: the char to be considered -- 2.35.1