File Index
Files and directories
Sign in
FILE

fan_hwmon.c

File preview with syntax highlighting.

linux-6.19.14/drivers/acpi/fan_hwmon.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * hwmon interface for the ACPI Fan driver.
  4 *
  5 * Copyright (C) 2024 Armin Wolf <W_Armin@gmx.de>
  6 */
  7
  8#include <linux/acpi.h>
  9#include <linux/device.h>
 10#include <linux/err.h>
 11#include <linux/hwmon.h>
 12#include <linux/limits.h>
 13#include <linux/types.h>
 14#include <linux/units.h>
 15
 16#include "fan.h"
 17
 18static struct acpi_fan_fps *acpi_fan_get_current_fps(struct acpi_fan *fan, u64 control)
 19{
 20	unsigned int i;
 21
 22	for (i = 0; i < fan->fps_count; i++) {
 23		if (fan->fps[i].control == control)
 24			return &fan->fps[i];
 25	}
 26
 27	return NULL;
 28}
 29
 30static umode_t acpi_fan_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
 31					 u32 attr, int channel)
 32{
 33	const struct acpi_fan *fan = drvdata;
 34	unsigned int i;
 35
 36	switch (type) {
 37	case hwmon_fan:
 38		switch (attr) {
 39		case hwmon_fan_input:
 40			return 0444;
 41		case hwmon_fan_target:
 42			/* Only acpi4 fans support fan control. */
 43			if (!fan->acpi4)
 44				return 0;
 45
 46			/*
 47			 * When in fine grain control mode, not every fan control value
 48			 * has an associated fan performance state.
 49			 */
 50			if (fan->fif.fine_grain_ctrl)
 51				return 0;
 52
 53			return 0444;
 54		default:
 55			return 0;
 56		}
 57	case hwmon_power:
 58		switch (attr) {
 59		case hwmon_power_input:
 60			/* Only acpi4 fans support fan control. */
 61			if (!fan->acpi4)
 62				return 0;
 63
 64			/*
 65			 * When in fine grain control mode, not every fan control value
 66			 * has an associated fan performance state.
 67			 */
 68			if (fan->fif.fine_grain_ctrl)
 69				return 0;
 70
 71			/*
 72			 * When all fan performance states contain no valid power data,
 73			 * when the associated attribute should not be created.
 74			 */
 75			for (i = 0; i < fan->fps_count; i++) {
 76				if (acpi_fan_power_valid(fan->fps[i].power))
 77					return 0444;
 78			}
 79
 80			return 0;
 81		default:
 82			return 0;
 83		}
 84	default:
 85		return 0;
 86	}
 87}
 88
 89static int acpi_fan_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
 90			       int channel, long *val)
 91{
 92	struct acpi_fan *fan = dev_get_drvdata(dev);
 93	struct acpi_fan_fps *fps;
 94	struct acpi_fan_fst fst;
 95	int ret;
 96
 97	ret = acpi_fan_get_fst(fan->handle, &fst);
 98	if (ret < 0)
 99		return ret;
100
101	switch (type) {
102	case hwmon_fan:
103		switch (attr) {
104		case hwmon_fan_input:
105			if (!acpi_fan_speed_valid(fst.speed))
106				return -ENODEV;
107
108			if (fst.speed > LONG_MAX)
109				return -EOVERFLOW;
110
111			*val = fst.speed;
112			return 0;
113		case hwmon_fan_target:
114			fps = acpi_fan_get_current_fps(fan, fst.control);
115			if (!fps)
116				return -EIO;
117
118			if (fps->speed > LONG_MAX)
119				return -EOVERFLOW;
120
121			*val = fps->speed;
122			return 0;
123		default:
124			return -EOPNOTSUPP;
125		}
126	case hwmon_power:
127		switch (attr) {
128		case hwmon_power_input:
129			fps = acpi_fan_get_current_fps(fan, fst.control);
130			if (!fps)
131				return -EIO;
132
133			if (!acpi_fan_power_valid(fps->power))
134				return -ENODEV;
135
136			if (fps->power > LONG_MAX / MICROWATT_PER_MILLIWATT)
137				return -EOVERFLOW;
138
139			*val = fps->power * MICROWATT_PER_MILLIWATT;
140			return 0;
141		default:
142			return -EOPNOTSUPP;
143		}
144	default:
145		return -EOPNOTSUPP;
146	}
147}
148
149static const struct hwmon_ops acpi_fan_hwmon_ops = {
150	.is_visible = acpi_fan_hwmon_is_visible,
151	.read = acpi_fan_hwmon_read,
152};
153
154static const struct hwmon_channel_info * const acpi_fan_hwmon_info[] = {
155	HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_TARGET),
156	HWMON_CHANNEL_INFO(power, HWMON_P_INPUT),
157	NULL
158};
159
160static const struct hwmon_chip_info acpi_fan_hwmon_chip_info = {
161	.ops = &acpi_fan_hwmon_ops,
162	.info = acpi_fan_hwmon_info,
163};
164
165void acpi_fan_notify_hwmon(struct device *dev)
166{
167	struct acpi_fan *fan = dev_get_drvdata(dev);
168
169	hwmon_notify_event(fan->hdev, hwmon_fan, hwmon_fan_input, 0);
170}
171
172int devm_acpi_fan_create_hwmon(struct device *dev)
173{
174	struct acpi_fan *fan = dev_get_drvdata(dev);
175
176	fan->hdev = devm_hwmon_device_register_with_info(dev, "acpi_fan", fan,
177							 &acpi_fan_hwmon_chip_info, NULL);
178
179	return PTR_ERR_OR_ZERO(fan->hdev);
180}
~/ Read-only file preview.