Skip to content

Commit bea2d98

Browse files
spandruvadarafaeljw
authored andcommitted
ACPI: fan: Properly handle fine grain control
When _FIF object specifies support for fine grain control, then fan speed can be set from 0 to 100% with the recommended minimum "step size" via _FSL object. Here the control value doesn't need to match any value from _FPS object. Currently we have a simple solution implemented which just pick maximum control value from _FPS to display the actual state, but this is not optimal when there is a big window between two control values in _FPS. Also there is no way to set to any speed which doesn't match control values in _FPS. The system firmware can start the fan at speed which doesn't match any control value. To support fine grain control (when supported) via thermal sysfs: - cooling device max state is not _FPS state count but it will be 100 / _FIF.step_size Step size can be from 1 to 9. - cooling device current state is _FST.control / _FIF.step_size - cooling device set state will set the control value cdev.curr_state * _FIF.step_size plus any adjustment for 100%. By the spec, when control value do not sum to 100% because of _FIF.step_size, OSPM may select an appropriate ending Level increment to reach 100%. There is no rounding during calculation. For example if step size is 6: thermal sysfs cooling device max_state = 100/6 = 16 So user can set any value from 0-16. If the system boots with a _FST.control which is not multiples of step_size, the thermal sysfs cur_state will be based on the range. For example for step size = 6: _FST.control thermal sysfs cur_state ------------------------------------------------ 0-5 0 6-11 1 .. .. 90-95 15 96-100 16 While setting the _FST.control, the compensation will be at the last step for cur_state = 16, which will set the _FST.control to 100. Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1 parent d445571 commit bea2d98

2 files changed

Lines changed: 74 additions & 26 deletions

File tree

drivers/acpi/fan.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ struct acpi_fan_fif {
3636
u8 low_speed_notification;
3737
};
3838

39+
struct acpi_fan_fst {
40+
u64 revision;
41+
u64 control;
42+
u64 speed;
43+
};
44+
3945
struct acpi_fan {
4046
bool acpi4;
4147
struct acpi_fan_fif fif;

drivers/acpi/fan_core.c

Lines changed: 68 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -63,20 +63,24 @@ static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long
6363
struct acpi_device *device = cdev->devdata;
6464
struct acpi_fan *fan = acpi_driver_data(device);
6565

66-
if (fan->acpi4)
67-
*state = fan->fps_count - 1;
68-
else
66+
if (fan->acpi4) {
67+
if (fan->fif.fine_grain_ctrl)
68+
*state = 100 / fan->fif.step_size;
69+
else
70+
*state = fan->fps_count - 1;
71+
} else {
6972
*state = 1;
73+
}
74+
7075
return 0;
7176
}
7277

73-
static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state)
78+
static int acpi_fan_get_fst(struct acpi_device *device, struct acpi_fan_fst *fst)
7479
{
7580
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
76-
struct acpi_fan *fan = acpi_driver_data(device);
7781
union acpi_object *obj;
7882
acpi_status status;
79-
int control, i;
83+
int ret = 0;
8084

8185
status = acpi_evaluate_object(device->handle, "_FST", NULL, &buffer);
8286
if (ACPI_FAILURE(status)) {
@@ -89,35 +93,52 @@ static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state)
8993
obj->package.count != 3 ||
9094
obj->package.elements[1].type != ACPI_TYPE_INTEGER) {
9195
dev_err(&device->dev, "Invalid _FST data\n");
92-
status = -EINVAL;
96+
ret = -EINVAL;
9397
goto err;
9498
}
9599

96-
control = obj->package.elements[1].integer.value;
100+
fst->revision = obj->package.elements[0].integer.value;
101+
fst->control = obj->package.elements[1].integer.value;
102+
fst->speed = obj->package.elements[2].integer.value;
103+
104+
err:
105+
kfree(obj);
106+
return ret;
107+
}
108+
109+
static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state)
110+
{
111+
struct acpi_fan *fan = acpi_driver_data(device);
112+
struct acpi_fan_fst fst;
113+
int status, i;
114+
115+
status = acpi_fan_get_fst(device, &fst);
116+
if (status)
117+
return status;
118+
119+
if (fan->fif.fine_grain_ctrl) {
120+
/* This control should be same what we set using _FSL by spec */
121+
if (fst.control > 100) {
122+
dev_dbg(&device->dev, "Invalid control value returned\n");
123+
goto match_fps;
124+
}
125+
126+
*state = (int) fst.control / fan->fif.step_size;
127+
return 0;
128+
}
129+
130+
match_fps:
97131
for (i = 0; i < fan->fps_count; i++) {
98-
/*
99-
* When Fine Grain Control is set, return the state
100-
* corresponding to maximum fan->fps[i].control
101-
* value compared to the current speed. Here the
102-
* fan->fps[] is sorted array with increasing speed.
103-
*/
104-
if (fan->fif.fine_grain_ctrl && control < fan->fps[i].control) {
105-
i = (i > 0) ? i - 1 : 0;
106-
break;
107-
} else if (control == fan->fps[i].control) {
132+
if (fst.control == fan->fps[i].control)
108133
break;
109-
}
110134
}
111135
if (i == fan->fps_count) {
112136
dev_dbg(&device->dev, "Invalid control value returned\n");
113-
status = -EINVAL;
114-
goto err;
137+
return -EINVAL;
115138
}
116139

117140
*state = i;
118141

119-
err:
120-
kfree(obj);
121142
return status;
122143
}
123144

@@ -161,12 +182,27 @@ static int fan_set_state_acpi4(struct acpi_device *device, unsigned long state)
161182
{
162183
struct acpi_fan *fan = acpi_driver_data(device);
163184
acpi_status status;
185+
u64 value = state;
186+
int max_state;
164187

165-
if (state >= fan->fps_count)
188+
if (fan->fif.fine_grain_ctrl)
189+
max_state = 100 / fan->fif.step_size;
190+
else
191+
max_state = fan->fps_count - 1;
192+
193+
if (state > max_state)
166194
return -EINVAL;
167195

168-
status = acpi_execute_simple_method(device->handle, "_FSL",
169-
fan->fps[state].control);
196+
if (fan->fif.fine_grain_ctrl) {
197+
value *= fan->fif.step_size;
198+
/* Spec allows compensate the last step only */
199+
if (value + fan->fif.step_size > 100)
200+
value = 100;
201+
} else {
202+
value = fan->fps[state].control;
203+
}
204+
205+
status = acpi_execute_simple_method(device->handle, "_FSL", value);
170206
if (ACPI_FAILURE(status)) {
171207
dev_dbg(&device->dev, "Failed to set state by _FSL\n");
172208
return -ENODEV;
@@ -238,6 +274,12 @@ static int acpi_fan_get_fif(struct acpi_device *device)
238274
fan->fif.step_size = fields[2];
239275
fan->fif.low_speed_notification = fields[3];
240276

277+
/* If there is a bug in step size and set as 0, change to 1 */
278+
if (!fan->fif.step_size)
279+
fan->fif.step_size = 1;
280+
/* If step size > 9, change to 9 (by spec valid values 1-9) */
281+
else if (fan->fif.step_size > 9)
282+
fan->fif.step_size = 9;
241283
err:
242284
kfree(obj);
243285
return status;

0 commit comments

Comments
 (0)