Skip to content

Commit 1597b37

Browse files
committed
hwmon: Add notification support
For hwmon drivers using the hwmon_device_register_with_info() API, it is desirable to have a generic notification mechanism available. This mechanism can be used to notify userspace as well as the thermal subsystem if the driver experiences any events, such as warning or critical alarms. Implement hwmon_notify_event() to provide this mechanism. The function generates a sysfs event and a udev event. If the device is registered with the thermal subsystem and the event is associated with a temperature sensor, also notify the thermal subsystem that a thermal event occurred. Signed-off-by: Guenter Roeck <[email protected]> Signed-off-by: Serge Semin <[email protected]> Cc: Maxim Kaurkin <[email protected]> Cc: Alexey Malahov <[email protected]> Cc: Thomas Bogendoerfer <[email protected]> Cc: Arnd Bergmann <[email protected]> Cc: Rob Herring <[email protected]> Cc: [email protected] Cc: [email protected] Signed-off-by: Guenter Roeck <[email protected]>
1 parent ddc65ca commit 1597b37

File tree

2 files changed

+68
-3
lines changed

2 files changed

+68
-3
lines changed

drivers/hwmon/hwmon.c

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <linux/gfp.h>
1616
#include <linux/hwmon.h>
1717
#include <linux/idr.h>
18+
#include <linux/list.h>
1819
#include <linux/module.h>
1920
#include <linux/pci.h>
2021
#include <linux/slab.h>
@@ -31,7 +32,7 @@ struct hwmon_device {
3132
const char *name;
3233
struct device dev;
3334
const struct hwmon_chip_info *chip;
34-
35+
struct list_head tzdata;
3536
struct attribute_group group;
3637
const struct attribute_group **groups;
3738
};
@@ -55,12 +56,12 @@ struct hwmon_device_attribute {
5556

5657
/*
5758
* Thermal zone information
58-
* In addition to the reference to the hwmon device,
59-
* also provides the sensor index.
6059
*/
6160
struct hwmon_thermal_data {
61+
struct list_head node; /* hwmon tzdata list entry */
6262
struct device *dev; /* Reference to hwmon device */
6363
int index; /* sensor index */
64+
struct thermal_zone_device *tzd;/* thermal zone device */
6465
};
6566

6667
static ssize_t
@@ -156,10 +157,17 @@ static const struct thermal_zone_of_device_ops hwmon_thermal_ops = {
156157
.get_temp = hwmon_thermal_get_temp,
157158
};
158159

160+
static void hwmon_thermal_remove_sensor(void *data)
161+
{
162+
list_del(data);
163+
}
164+
159165
static int hwmon_thermal_add_sensor(struct device *dev, int index)
160166
{
167+
struct hwmon_device *hwdev = to_hwmon_device(dev);
161168
struct hwmon_thermal_data *tdata;
162169
struct thermal_zone_device *tzd;
170+
int err;
163171

164172
tdata = devm_kzalloc(dev, sizeof(*tdata), GFP_KERNEL);
165173
if (!tdata)
@@ -177,6 +185,13 @@ static int hwmon_thermal_add_sensor(struct device *dev, int index)
177185
if (IS_ERR(tzd) && (PTR_ERR(tzd) != -ENODEV))
178186
return PTR_ERR(tzd);
179187

188+
err = devm_add_action(dev, hwmon_thermal_remove_sensor, &tdata->node);
189+
if (err)
190+
return err;
191+
192+
tdata->tzd = tzd;
193+
list_add(&tdata->node, &hwdev->tzdata);
194+
180195
return 0;
181196
}
182197

@@ -211,11 +226,27 @@ static int hwmon_thermal_register_sensors(struct device *dev)
211226
return 0;
212227
}
213228

229+
static void hwmon_thermal_notify(struct device *dev, int index)
230+
{
231+
struct hwmon_device *hwdev = to_hwmon_device(dev);
232+
struct hwmon_thermal_data *tzdata;
233+
234+
list_for_each_entry(tzdata, &hwdev->tzdata, node) {
235+
if (tzdata->index == index) {
236+
thermal_zone_device_update(tzdata->tzd,
237+
THERMAL_EVENT_UNSPECIFIED);
238+
}
239+
}
240+
}
241+
214242
#else
215243
static int hwmon_thermal_register_sensors(struct device *dev)
216244
{
217245
return 0;
218246
}
247+
248+
static void hwmon_thermal_notify(struct device *dev, int index) { }
249+
219250
#endif /* IS_REACHABLE(CONFIG_THERMAL) && ... */
220251

221252
static int hwmon_attr_base(enum hwmon_sensor_types type)
@@ -543,6 +574,35 @@ static const int __templates_size[] = {
543574
[hwmon_intrusion] = ARRAY_SIZE(hwmon_intrusion_attr_templates),
544575
};
545576

577+
int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type,
578+
u32 attr, int channel)
579+
{
580+
char sattr[MAX_SYSFS_ATTR_NAME_LENGTH];
581+
const char * const *templates;
582+
const char *template;
583+
int base;
584+
585+
if (type >= ARRAY_SIZE(__templates))
586+
return -EINVAL;
587+
if (attr >= __templates_size[type])
588+
return -EINVAL;
589+
590+
templates = __templates[type];
591+
template = templates[attr];
592+
593+
base = hwmon_attr_base(type);
594+
595+
scnprintf(sattr, MAX_SYSFS_ATTR_NAME_LENGTH, template, base + channel);
596+
sysfs_notify(&dev->kobj, NULL, sattr);
597+
kobject_uevent(&dev->kobj, KOBJ_CHANGE);
598+
599+
if (type == hwmon_temp)
600+
hwmon_thermal_notify(dev, channel);
601+
602+
return 0;
603+
}
604+
EXPORT_SYMBOL_GPL(hwmon_notify_event);
605+
546606
static int hwmon_num_channel_attrs(const struct hwmon_channel_info *info)
547607
{
548608
int i, n;
@@ -693,6 +753,8 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata,
693753
if (err)
694754
goto free_hwmon;
695755

756+
INIT_LIST_HEAD(&hwdev->tzdata);
757+
696758
if (dev && dev->of_node && chip && chip->ops->read &&
697759
chip->info[0]->type == hwmon_chip &&
698760
(chip->info[0]->config[0] & HWMON_C_REGISTER_TZ)) {

include/linux/hwmon.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,9 @@ devm_hwmon_device_register_with_info(struct device *dev,
436436
void hwmon_device_unregister(struct device *dev);
437437
void devm_hwmon_device_unregister(struct device *dev);
438438

439+
int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type,
440+
u32 attr, int channel);
441+
439442
/**
440443
* hwmon_is_bad_char - Is the char invalid in a hwmon name
441444
* @ch: the char to be considered

0 commit comments

Comments
 (0)