Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/acpi/fan_hwmon.c
26278 views
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 <[email protected]>
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
18
/* Returned when the ACPI fan does not support speed reporting */
19
#define FAN_SPEED_UNAVAILABLE U32_MAX
20
#define FAN_POWER_UNAVAILABLE U32_MAX
21
22
static struct acpi_fan_fps *acpi_fan_get_current_fps(struct acpi_fan *fan, u64 control)
23
{
24
unsigned int i;
25
26
for (i = 0; i < fan->fps_count; i++) {
27
if (fan->fps[i].control == control)
28
return &fan->fps[i];
29
}
30
31
return NULL;
32
}
33
34
static umode_t acpi_fan_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
35
u32 attr, int channel)
36
{
37
const struct acpi_fan *fan = drvdata;
38
unsigned int i;
39
40
switch (type) {
41
case hwmon_fan:
42
switch (attr) {
43
case hwmon_fan_input:
44
return 0444;
45
case hwmon_fan_target:
46
/* Only acpi4 fans support fan control. */
47
if (!fan->acpi4)
48
return 0;
49
50
/*
51
* When in fine grain control mode, not every fan control value
52
* has an associated fan performance state.
53
*/
54
if (fan->fif.fine_grain_ctrl)
55
return 0;
56
57
return 0444;
58
default:
59
return 0;
60
}
61
case hwmon_power:
62
switch (attr) {
63
case hwmon_power_input:
64
/* Only acpi4 fans support fan control. */
65
if (!fan->acpi4)
66
return 0;
67
68
/*
69
* When in fine grain control mode, not every fan control value
70
* has an associated fan performance state.
71
*/
72
if (fan->fif.fine_grain_ctrl)
73
return 0;
74
75
/*
76
* When all fan performance states contain no valid power data,
77
* when the associated attribute should not be created.
78
*/
79
for (i = 0; i < fan->fps_count; i++) {
80
if (fan->fps[i].power != FAN_POWER_UNAVAILABLE)
81
return 0444;
82
}
83
84
return 0;
85
default:
86
return 0;
87
}
88
default:
89
return 0;
90
}
91
}
92
93
static int acpi_fan_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
94
int channel, long *val)
95
{
96
struct acpi_device *adev = to_acpi_device(dev->parent);
97
struct acpi_fan *fan = dev_get_drvdata(dev);
98
struct acpi_fan_fps *fps;
99
struct acpi_fan_fst fst;
100
int ret;
101
102
ret = acpi_fan_get_fst(adev, &fst);
103
if (ret < 0)
104
return ret;
105
106
switch (type) {
107
case hwmon_fan:
108
switch (attr) {
109
case hwmon_fan_input:
110
if (fst.speed == FAN_SPEED_UNAVAILABLE)
111
return -ENODEV;
112
113
if (fst.speed > LONG_MAX)
114
return -EOVERFLOW;
115
116
*val = fst.speed;
117
return 0;
118
case hwmon_fan_target:
119
fps = acpi_fan_get_current_fps(fan, fst.control);
120
if (!fps)
121
return -EIO;
122
123
if (fps->speed > LONG_MAX)
124
return -EOVERFLOW;
125
126
*val = fps->speed;
127
return 0;
128
default:
129
return -EOPNOTSUPP;
130
}
131
case hwmon_power:
132
switch (attr) {
133
case hwmon_power_input:
134
fps = acpi_fan_get_current_fps(fan, fst.control);
135
if (!fps)
136
return -EIO;
137
138
if (fps->power == FAN_POWER_UNAVAILABLE)
139
return -ENODEV;
140
141
if (fps->power > LONG_MAX / MICROWATT_PER_MILLIWATT)
142
return -EOVERFLOW;
143
144
*val = fps->power * MICROWATT_PER_MILLIWATT;
145
return 0;
146
default:
147
return -EOPNOTSUPP;
148
}
149
default:
150
return -EOPNOTSUPP;
151
}
152
}
153
154
static const struct hwmon_ops acpi_fan_hwmon_ops = {
155
.is_visible = acpi_fan_hwmon_is_visible,
156
.read = acpi_fan_hwmon_read,
157
};
158
159
static const struct hwmon_channel_info * const acpi_fan_hwmon_info[] = {
160
HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_TARGET),
161
HWMON_CHANNEL_INFO(power, HWMON_P_INPUT),
162
NULL
163
};
164
165
static const struct hwmon_chip_info acpi_fan_hwmon_chip_info = {
166
.ops = &acpi_fan_hwmon_ops,
167
.info = acpi_fan_hwmon_info,
168
};
169
170
int devm_acpi_fan_create_hwmon(struct acpi_device *device)
171
{
172
struct acpi_fan *fan = acpi_driver_data(device);
173
struct device *hdev;
174
175
hdev = devm_hwmon_device_register_with_info(&device->dev, "acpi_fan", fan,
176
&acpi_fan_hwmon_chip_info, NULL);
177
return PTR_ERR_OR_ZERO(hdev);
178
}
179
180