Hello,
ACPI can control fans, but it is up to the hardware manufacturer (OEM) to implement it.
_______________________________
Step 1. Check for ACPI fan control
The OEM needs to add a device with id "PNP0C0B" to the acpi namespace.
In FreeBSD, you can test this with the command: acpidump -d -t | grep PNP0C0B
On Windows, you can download acpi tools at https://acpica.org/downloads/binary-tools
Then use the command: acpidump.exe | findstr PNP0C0B
Some fans use IDs which are not documented in the ACPI specification:
"PNP0C0B", /* Generic Fan */ \
"INT3404", /* Fan */ \
"INTC1044", /* Fan for Tiger Lake generation */
"INTC1048", /* Fan for Alder Lake generation */
"INTC1063", /* Fan for Meteor Lake generation */
"INTC10A2", /* Fan for Raptor Lake generation */
"INT3404", /* Fan */ \
"INTC1044", /* Fan for Tiger Lake generation */
"INTC1048", /* Fan for Alder Lake generation */
"INTC1063", /* Fan for Meteor Lake generation */
"INTC10A2", /* Fan for Raptor Lake generation */
You might want to search for these strings as well.
__________________________
Step 2. Check for version of ACPI
Fan version is either 1.0 or 4.0.
a) Acpi version 1.0
If you suceed with step 1., then you can communicate with the fan. You can turn it on and off
by putting it in power state D0 or D3.
In C, you would probably use acpi_set_powerstate(dev, ACPI_STATE_D3),
or maybe use acpi power methods, like _ON, _OFF, _PR3, _PR0 (haven't tested it).
Or maybe an alternative: There is a suggestion on FreeBSD acpi wiki:
device_power -- Add a "power" argument to devctl(8) that allows a device to be set into various low power or off states.
Noone has implemented that yet ("not done"). :)
b) ACPI version 4.0
To check, whether your fan supports fan levels, you can do this:
OEM _must_ provide four predefined acpi methods. They are described in detail in the acpi
specification. They are called: _FIF, _FPS, _FSL, _FST
So just use:
acpidump -d -t | grep _FPS
and so on. If all four are present, you can use fan levels! :-)
In your source code, it could look like this:
ACPI_HANDLE handle;
ACPI_HANDLE tmp;
ACPI_HANDLE tmp;
/* fans are either acpi 1.0 or 4.0 compatible, so check now. */
if (ACPI_SUCCESS(acpi_get_handle(handle, "_FIF", &tmp)) &&
ACPI_SUCCESS(acpi_get_handle(handle, "_FPS", &tmp)) &&
ACPI_SUCCESS(acpi_get_handle(handle, "_FSL", &tmp)) &&
ACPI_SUCCESS(acpi_get_handle(handle, "_FST", &tmp)))
acpi_fan_initiate_acpi4(dev);
if (ACPI_SUCCESS(acpi_get_handle(handle, "_FIF", &tmp)) &&
ACPI_SUCCESS(acpi_get_handle(handle, "_FPS", &tmp)) &&
ACPI_SUCCESS(acpi_get_handle(handle, "_FSL", &tmp)) &&
ACPI_SUCCESS(acpi_get_handle(handle, "_FST", &tmp)))
acpi_fan_initiate_acpi4(dev);
else /* nothing to do in acpi version 1, really */
acpi_fan_softc.version = 1;
acpi_fan_softc.version = 1;
3. How to set fan levels
As a driver author, you'd need to decide how you'd want to implement the fan level. It can be done
via /dev/acpi (and also add code to acpiconf (8)). Or it can be done via systctls.
So in your code, you could add a proc sysctl. There are multiple ways to implement sysctls,
one way could be this:
sysctl_ctx_init(&clist); /* sysctl context */
struct sysctl_oid *fan_oid = device_get_sysctl_tree(dev);
SYSCTL_ADD_PROC(&clist, SYSCTL_CHILDREN(fan_oid), OID_AUTO,
"Fan level", CTLTYPE_INT | CTLFLAG_RW, 0, 0,
acpi_fan_level_sysctl, "I" ,"Fan level");
Or whatever code you like.
Then you need a sysctl handler:
static int
acpi_fan_level_sysctl(SYSCTL_HANDLER_ARGS) {
acpi_fan_level_sysctl(SYSCTL_HANDLER_ARGS) {
...
}
In the handler function you could "handle" the fan level, and probably call
acpi_evaluate_object() on the _FIF, _FPS, _FSL, and _FST methods.
Unfortunately, my laptops don't support fans at all. So I can't really write a fan driver.
I think it is a good beginners task.
Basically, if your fan has three or four pins, it might support fan levels. The first two pins are
used for electricity. The third pin is used to upscale or downscale the power (voltage?),
thus increasing or decreasing the fan speed. There is no magic to this.
4. Sceleton acpi driver
If you need a sceleton acpi driver, that shouldn't be a problem.
FreeBSD puts all acpi drivers (modules) into the acpi subsystem (sys/dev/acpica), instead
of giving them a separate makefile in sys/modules.
This was my first attempt, without ever testing anything (bugs to be expected): :-)
#include "opt_acpi.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
/* for testing, aka printf */
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <contrib/dev/acpica/include/acpi.h>
#include <dev/acpica/acpivar.h>
#include <dev/acpica/acpiio.h>
#include <dev/acpica/acpiio.h>
/* Hooks for the ACPI CA debugging infrastructure */
#define _COMPONENT ACPI_FAN
ACPI_MODULE_NAME("FAN")
#define _COMPONENT ACPI_FAN
ACPI_MODULE_NAME("FAN")
/* driver software context */
struct acpi_fan_softc {
device_t dev;
int version; /* either ACPI 1.0 or 4.0 */
};
struct acpi_fan_softc {
device_t dev;
int version; /* either ACPI 1.0 or 4.0 */
};
static device_method_t acpi_fan_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, acpi_fan_probe),
DEVMETHOD(device_attach, acpi_fan_attach),
DEVMETHOD(device_detach, acpi_fan_detach),
/* Device interface */
DEVMETHOD(device_probe, acpi_fan_probe),
DEVMETHOD(device_attach, acpi_fan_attach),
DEVMETHOD(device_detach, acpi_fan_detach),
DEVMETHOD_END
};
};
static int
acpi_fan_probe(device_t dev)
{
static char *fan_ids[] = { \
"PNP0C0B", /* Generic Fan */ \
"INT3404", /* Fan */ \
"INTC1044", /* Fan for Tiger Lake generation */ \
"INTC1048", /* Fan for Alder Lake generation */ \
"INTC1063", /* Fan for Meteor Lake generation */ \
"INTC10A2", /* Fan for Raptor Lake generation */ \
NULL };
int rv;
if (acpi_disabled("fan"))
return (ENXIO);
rv = ACPI_ID_PROBE(device_get_parent(dev), dev, fan_ids, NULL);
if (rv <= 0)
device_set_desc(dev, "ACPI FAN");
return (rv);
}
acpi_fan_probe(device_t dev)
{
static char *fan_ids[] = { \
"PNP0C0B", /* Generic Fan */ \
"INT3404", /* Fan */ \
"INTC1044", /* Fan for Tiger Lake generation */ \
"INTC1048", /* Fan for Alder Lake generation */ \
"INTC1063", /* Fan for Meteor Lake generation */ \
"INTC10A2", /* Fan for Raptor Lake generation */ \
NULL };
int rv;
if (acpi_disabled("fan"))
return (ENXIO);
rv = ACPI_ID_PROBE(device_get_parent(dev), dev, fan_ids, NULL);
if (rv <= 0)
device_set_desc(dev, "ACPI FAN");
return (rv);
}
static int
acpi_fan_attach(device_t dev)
{
int error;
ACPI_HANDLE handle;
ACPI_HANDLE tmp;
struct acpi_fan_softc *sc;
acpi_fan_attach(device_t dev)
{
int error;
ACPI_HANDLE handle;
ACPI_HANDLE tmp;
struct acpi_fan_softc *sc;
sc = device_get_softc(dev);
handle = acpi_get_handle(dev);
/* fans are either acpi 1.0 or 4.0 compatible, so check now. */
if (ACPI_SUCCESS(acpi_get_handle(handle, "_FIF", &tmp)) &&
ACPI_SUCCESS(acpi_get_handle(handle, "_FPS", &tmp)) &&
ACPI_SUCCESS(acpi_get_handle(handle, "_FSL", &tmp)) &&
ACPI_SUCCESS(acpi_get_handle(handle, "_FST", &tmp)))
acpi_fan_softc.version = 4;
else /* nothing to do in acpi version 1, really */
acpi_fan_softc.version = 1;
if (ACPI_SUCCESS(acpi_get_handle(handle, "_FIF", &tmp)) &&
ACPI_SUCCESS(acpi_get_handle(handle, "_FPS", &tmp)) &&
ACPI_SUCCESS(acpi_get_handle(handle, "_FSL", &tmp)) &&
ACPI_SUCCESS(acpi_get_handle(handle, "_FST", &tmp)))
acpi_fan_softc.version = 4;
else /* nothing to do in acpi version 1, really */
acpi_fan_softc.version = 1;
return 0;
}
}
static int
acpi_fan_detach(device_t dev) {
sysctl_ctx_free(&clist);
return 0;
}
acpi_fan_detach(device_t dev) {
sysctl_ctx_free(&clist);
return 0;
}
static driver_t acpi_fan_driver = {
"fan",
acpi_fan_methods,
sizeof(struct acpi_fan_softc),
};
DRIVER_MODULE(acpi_fan, acpi, acpi_fan_driver, 0, 0);
MODULE_DEPEND(acpi_fan, acpi, 1, 1, 1);
MODULE_DEPEND(acpi_fan, acpi, 1, 1, 1);
_____________________
5. How linux does it
You can check linux fan driver on github:
https://github.com/torvalds/linux/tree/master/drivers/acpi
They separate the driver into three files.
fan.h
fan_attr.c
fan_core.c
It's ok to learn from linux. :-)
Sorry for long message.
Georg.
Gesendet: Mittwoch, 03. Mai 2023 um 02:26 Uhr
Von: "Dmitry N. Medvedev" <dmitry.medve...@gmail.com>
An: "Adrian Chadd" <adr...@freebsd.org>
Cc: freebsd-acpi@freebsd.org
Betreff: Re: managing a fan speed via memory address
Von: "Dmitry N. Medvedev" <dmitry.medve...@gmail.com>
An: "Adrian Chadd" <adr...@freebsd.org>
Cc: freebsd-acpi@freebsd.org
Betreff: Re: managing a fan speed via memory address
good morning Adrian,
1. I am just learning :) Not 100% sure ACPI has anything to do with fan control ( still it looks that it actually does )
-- found the Advanced Configuration and Power Interface Specification PDF. Will spend some time grasping the ideas
2. to quickly write any driver I will have to first find out how to do it :) any guidance ( preferable in textual form will be greatly appreciated ) will learn it :)
3. there isn't a single thermal sensor, but the SAS disks report their temperatures
( via dmidecode if I am not mistaken, or some other program -- I will be more sure tomorrow morning ).
so, theoretically I could be able to read their temperature and decide if I would like to send more power to the fan.
On Wed, May 3, 2023 at 2:14 AM Adrian Chadd <adr...@freebsd.org> wrote:
Is it not an ACPI driver? If not, you could write a quick fan driver!Is there a thermal sensor(s) you could read to see how warm they get?-adrianOn Tue, 2 May 2023 at 17:06, Dmitry N. Medvedev <dmitry.medve...@gmail.com> wrote:good morning,Recently I have learned about the dmidecode program and found the address of the FRNTFAN port in my HP Z420 machine: 0x0037.Since I am a complete newbie, I would like to learn if there is a way to read and change the value at this address.I need a bit of guidance.The context: I have added 8 SAS disks to the machine, put noctua fan in front of them and connected the fan to the FRNTFAN port on the motherboard.It looks like the fan works, but I am sure the disks would benefit if the fan produced more pressure. Which I fantasize I could do via changing the value at the said address.Not sure, of course.best regards,Dmitry N. Medvedev