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