// SPDX-License-Identifier: GPL-2.0-only
/*
* hwmon.c - part of lm_sensors, Linux kernel modules for hardware monitoring
*
* This file defines the sysfs class "hwmon", for use by sensors drivers.
*
* Copyright (C) 2005 Mark M. Hoffman <mhoffman@lightlink.com>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gfp.h>
#include <linux/hwmon.h>
#include <linux/idr.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/thermal.h>
#define CREATE_TRACE_POINTS
#include <trace/events/hwmon.h>
#define HWMON_ID_PREFIX "hwmon"
#define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d"
struct hwmon_device {
const char *name;
const char *label;
struct device dev;
const struct hwmon_chip_info *chip;
struct list_head tzdata;
struct attribute_group group;
const struct attribute_group **groups;
};
#define to_hwmon_device(d) container_of(d, struct hwmon_device, dev)
#define MAX_SYSFS_ATTR_NAME_LENGTH 32
struct hwmon_device_attribute {
struct device_attribute dev_attr;
const struct hwmon_ops *ops;
enum hwmon_sensor_types type;
u32 attr;
int index;
char name[MAX_SYSFS_ATTR_NAME_LENGTH];
};
#define to_hwmon_attr(d) \
container_of(d, struct hwmon_device_attribute, dev_attr)
#define to_dev_attr(a) container_of(a, struct device_attribute, attr)
/*
* Thermal zone information
*/
struct hwmon_thermal_data {
struct list_head node; /* hwmon tzdata list entry */
struct device *dev; /* Reference to hwmon device */
int index; /* sensor index */
struct thermal_zone_device *tzd;/* thermal zone device */
};
static ssize_t
name_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", to_hwmon_device(dev)->name);
}
static DEVICE_ATTR_RO(name);
static ssize_t
label_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sysfs_emit(buf, "%s\n", to_hwmon_device(dev)->label);
}
static DEVICE_ATTR_RO(label);
static struct attribute *hwmon_dev_attrs[] = {
&dev_attr_name.attr,
&dev_attr_label.attr,
NULL
};
static umode_t hwmon_dev_attr_is_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
struct device *dev = kobj_to_dev(kobj);
struct hwmon_device *hdev = to_hwmon_device(dev);
if (attr == &dev_attr_name.attr && hdev->name == NULL)
return 0;
if (attr == &dev_attr_label.attr && hdev->label == NULL)
return 0;
return attr->mode;
}
static const struct attribute_group hwmon_dev_attr_group = {
.attrs = hwmon_dev_attrs,
.is_visible = hwmon_dev_attr_is_visible,
};
static const struct attribute_group *hwmon_dev_attr_groups[] = {
&hwmon_dev_attr_group,
NULL
};
static void hwmon_free_attrs(struct attribute **attrs)
{
int i;
for (i = 0; attrs[i]; i++) {
struct device_attribute *dattr = to_dev_attr(attrs[i]);
struct hwmon_device_attribute *hattr = to_hwmon_attr(dattr);
kfree(hattr);
}
kfree(attrs);
}
static void hwmon_dev_release(struct device *dev)
{
struct hwmon_device *hwdev = to_hwmon_device(dev);
if (hwdev->group.attrs)
hwmon_free_attrs(hwdev->group.attrs);
kfree(hwdev->groups);
kfree(hwdev->label);
kfree(hwdev);
}
static struct class hwmon_class = {
.name = "hwmon",
.owner = THIS_MODULE,
.dev_groups = hwmon_dev_attr_groups,
.dev_release = hwmon_dev_release,
};
static DEFINE_IDA(hwmon_ida);
/* Thermal zone handling */
/*
* The complex conditional is necessary to avoid a cyclic dependency
* between hwmon and thermal_sys modules.
*/
#ifdef CONFIG_THERMAL_OF
static int hwmon_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
{
struct