Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/acpi/fan_hwmon.c
51031 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
static 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
30
static 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
89
static 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
149
static const struct hwmon_ops acpi_fan_hwmon_ops = {
150
.is_visible = acpi_fan_hwmon_is_visible,
151
.read = acpi_fan_hwmon_read,
152
};
153
154
static 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
160
static const struct hwmon_chip_info acpi_fan_hwmon_chip_info = {
161
.ops = &acpi_fan_hwmon_ops,
162
.info = acpi_fan_hwmon_info,
163
};
164
165
void 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
172
int 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
}
181
182