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>