Aw: Re: managing a fan speed via memory address

From: Georg Lindenberg <georg.lindenberg_at_web.de>
Date: Wed, 03 May 2023 18:02:25 UTC
<html><head></head><body><div style="font-family: Verdana;font-size: 12.0px;"><div>&nbsp;</div>

<div>
<div>Hello,</div>

<div>&nbsp;</div>

<div>ACPI can control fans, but it is up to the hardware manufacturer (OEM) to implement it.</div>

<div>&nbsp;</div>

<div>_______________________________</div>

<div>Step 1. Check for ACPI fan control</div>

<div>&nbsp;</div>

<div>The OEM needs to add a device with id &quot;PNP0C0B&quot; to the acpi namespace.</div>

<div>In FreeBSD, you can test this with the command: acpidump -d -t &#124; grep PNP0C0B</div>

<div>&nbsp;</div>

<div>On Windows, you can download acpi tools at https://acpica.org/downloads/binary-tools</div>

<div>Then use the command: acpidump.exe &#124; findstr PNP0C0B</div>

<div>&nbsp;</div>

<div>Some fans use IDs which are not documented in the ACPI specification:</div>

<div>&nbsp;&nbsp;&nbsp; &quot;PNP0C0B&quot;, &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;/* Generic Fan */ &#92;<br/>
&nbsp;&nbsp; &nbsp;&quot;INT3404&quot;, &nbsp; &nbsp;&nbsp;&nbsp;&nbsp; /* Fan */ &#92;<br/>
&nbsp;&nbsp; &nbsp;&quot;INTC1044&quot;, &nbsp; &nbsp;&nbsp;&nbsp;&nbsp; /* Fan for Tiger Lake generation */<br/>
&nbsp;&nbsp; &nbsp;&quot;INTC1048&quot;, &nbsp;&nbsp;&nbsp; /* Fan for Alder Lake generation */<br/>
&nbsp;&nbsp; &nbsp;&quot;INTC1063&quot;, &nbsp;&nbsp; /* Fan for Meteor Lake generation */<br/>
&nbsp;&nbsp; &nbsp;&quot;INTC10A2&quot;, &nbsp;&nbsp; /* Fan for Raptor Lake generation */</div>

<div>
<div>&nbsp;</div>

<div>You might want to search for these strings as well.</div>

<div>&nbsp;</div>

<div>__________________________</div>

<div>Step 2. Check for version of ACPI</div>

<div>&nbsp;</div>

<div>Fan version is either 1.0 or 4.0.</div>

<div>&nbsp;</div>

<div>a) Acpi version 1.0</div>

<div>&nbsp;</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&#39;t tested it).</div>

<div>&nbsp;</div>

<div>Or maybe an alternative: There is a suggestion on FreeBSD acpi wiki:</div>

<div>device_power -- Add a &quot;power&quot; 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 (&quot;not done&quot;). :)</div>

<div>&nbsp;</div>

<div>b) ACPI version 4.0</div>

<div>&nbsp;</div>

<div>To check, whether your fan supports fan levels, you can do this:</div>

<div>&nbsp;</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 &#124; grep _FPS</div>

<div>and so on. If all four are present, you can use fan levels! :-)</div>

<div>&nbsp;</div>

<div>In your source code, it could look like this:</div>

<div>&nbsp;</div>

<div>&nbsp;&nbsp;&nbsp; ACPI_HANDLE&nbsp;&nbsp; &nbsp;handle;<br/>
&nbsp;&nbsp; &nbsp;ACPI_HANDLE tmp;</div>

<div>&nbsp;</div>

<div>&nbsp;&nbsp;&nbsp; /* fans are either acpi 1.0 or 4.0 compatible, so check now. */<br/>
&nbsp;&nbsp;&nbsp; &nbsp;if (ACPI_SUCCESS(acpi_get_handle(handle, &quot;_FIF&quot;, &amp;tmp)) &amp;&amp;<br/>
&nbsp;&nbsp; &nbsp;ACPI_SUCCESS(acpi_get_handle(handle, &quot;_FPS&quot;, &amp;tmp)) &amp;&amp;<br/>
&nbsp;&nbsp; &nbsp;ACPI_SUCCESS(acpi_get_handle(handle, &quot;_FSL&quot;, &amp;tmp)) &amp;&amp;<br/>
&nbsp;&nbsp; &nbsp;ACPI_SUCCESS(acpi_get_handle(handle, &quot;_FST&quot;, &amp;tmp))) &nbsp;&nbsp;<br/>
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;acpi_fan_initiate_acpi4(dev);</div>

<div>&nbsp;</div>

<div>
<div>&nbsp;&nbsp;&nbsp; else&nbsp;&nbsp; &nbsp;/* nothing to do in acpi version 1, really */<br/>
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;acpi_fan_softc.version = 1;</div>

<div>&nbsp;</div>

<div>3. How to set fan levels</div>

<div>&nbsp;</div>

<div>As a driver author, you&#39;d need to decide how you&#39;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>&nbsp;</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>&nbsp;</div>

<div>&nbsp;&nbsp;&nbsp; sysctl_ctx_init(&amp;clist);&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;/* sysctl context */</div>

<div><br/>
&nbsp;&nbsp; &nbsp;struct sysctl_oid *fan_oid = device_get_sysctl_tree(dev);<br/>
&nbsp;&nbsp; &nbsp;SYSCTL_ADD_PROC(&amp;clist, SYSCTL_CHILDREN(fan_oid), OID_AUTO,<br/>
&nbsp;&nbsp; &nbsp;&quot;Fan level&quot;, CTLTYPE_INT &#124; CTLFLAG_RW, 0, 0,<br/>
&nbsp;&nbsp; &nbsp;acpi_fan_level_sysctl, &quot;I&quot; ,&quot;Fan level&quot;);</div>

<div>&nbsp;</div>

<div>Or whatever code you like.</div>

<div>&nbsp;</div>

<div>Then you need a sysctl handler:</div>

<div>&nbsp;</div>

<div>static int<br/>
acpi_fan_level_sysctl(SYSCTL_HANDLER_ARGS) {</div>

<div>&nbsp;</div>

<div>...</div>

<div>}</div>

<div>&nbsp;</div>

<div>In the handler function you could &quot;handle&quot; the fan level, and probably call</div>

<div>acpi_evaluate_object() on the _FIF, _FPS, _FSL, and _FST methods.</div>

<div>&nbsp;</div>

<div>Unfortunately, my laptops don&#39;t support fans at all. So I can&#39;t really write a fan driver.</div>

<div>I think it is a good beginners task.</div>

<div>&nbsp;</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>&nbsp;</div>

<div>4. Sceleton acpi driver</div>

<div>&nbsp;</div>

<div>If you need a sceleton acpi driver, that shouldn&#39;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>&nbsp;</div>

<div>This was my first attempt, without ever testing anything (bugs to be expected): :-)</div>

<div>&nbsp;</div>

<div>
<div>#include &quot;opt_acpi.h&quot;<br/>
#include &lt;sys/param.h&gt;<br/>
#include &lt;sys/kernel.h&gt;<br/>
#include &lt;sys/module.h&gt;</div>

<div>&nbsp;</div>

<div>/* for testing, aka printf */<br/>
#include &lt;sys/types.h&gt;<br/>
#include &lt;sys/systm.h&gt;</div>

<div>&nbsp;</div>

<div>#include &lt;contrib/dev/acpica/include/acpi.h&gt;</div>

<div>#include &lt;dev/acpica/acpivar.h&gt;<br/>
#include &lt;dev/acpica/acpiio.h&gt;</div>

<div>&nbsp;</div>

<div>/* Hooks for the ACPI CA debugging infrastructure */<br/>
#define&nbsp;&nbsp; &nbsp;_COMPONENT&nbsp;&nbsp; &nbsp;ACPI_FAN<br/>
ACPI_MODULE_NAME(&quot;FAN&quot;)</div>
&nbsp;

<div>
<div>/* driver software context */<br/>
struct acpi_fan_softc {<br/>
&nbsp;&nbsp; &nbsp;device_t&nbsp;&nbsp; &nbsp;dev;<br/>
&nbsp;&nbsp; &nbsp;int&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;version;&nbsp;&nbsp; &nbsp;/* either ACPI 1.0 or 4.0 */<br/>
};</div>
</div>
</div>

<div>&nbsp;</div>

<div>
<div>static device_method_t acpi_fan_methods[] = {<br/>
&nbsp;&nbsp;&nbsp; /* Device interface */<br/>
&nbsp;&nbsp;&nbsp; DEVMETHOD(device_probe,&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;acpi_fan_probe),<br/>
&nbsp;&nbsp;&nbsp; DEVMETHOD(device_attach,&nbsp;&nbsp; &nbsp;acpi_fan_attach),<br/>
&nbsp;&nbsp;&nbsp; DEVMETHOD(device_detach,&nbsp;&nbsp; &nbsp;acpi_fan_detach),</div>

<div>&nbsp;&nbsp;&nbsp; DEVMETHOD_END<br/>
};</div>

<div>&nbsp;</div>

<div>static int<br/>
acpi_fan_probe(device_t dev)<br/>
{<br/>
&nbsp;&nbsp;&nbsp; static char *fan_ids[] = { &#92;<br/>
&nbsp;&nbsp; &nbsp;&quot;PNP0C0B&quot;, &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;/* Generic Fan */ &#92;<br/>
&nbsp;&nbsp; &nbsp;&quot;INT3404&quot;,&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;/* Fan */ &#92;<br/>
&nbsp;&nbsp; &nbsp;&quot;INTC1044&quot;,&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;/* Fan for Tiger Lake generation */ &#92;<br/>
&nbsp;&nbsp; &nbsp;&quot;INTC1048&quot;, &nbsp;&nbsp; &nbsp;/* Fan for Alder Lake generation */ &#92;<br/>
&nbsp;&nbsp; &nbsp;&quot;INTC1063&quot;, &nbsp;&nbsp; &nbsp;/* Fan for Meteor Lake generation */ &#92;<br/>
&nbsp;&nbsp; &nbsp;&quot;INTC10A2&quot;, &nbsp;&nbsp; &nbsp;/* Fan for Raptor Lake generation */ &#92;<br/>
&nbsp;&nbsp; &nbsp;NULL };<br/>
&nbsp;&nbsp;&nbsp; int rv;<br/>
&nbsp;&nbsp; &nbsp;<br/>
&nbsp;&nbsp;&nbsp; if (acpi_disabled(&quot;fan&quot;))<br/>
&nbsp;&nbsp; &nbsp;return (ENXIO);<br/>
&nbsp;&nbsp;&nbsp; rv = ACPI_ID_PROBE(device_get_parent(dev), dev, fan_ids, NULL);<br/>
&nbsp;&nbsp;&nbsp; if (rv &lt;= 0)<br/>
&nbsp;&nbsp; &nbsp;device_set_desc(dev, &quot;ACPI FAN&quot;);<br/>
&nbsp;&nbsp;&nbsp; return (rv);<br/>
}</div>

<div>&nbsp;</div>

<div>
<div>static int<br/>
acpi_fan_attach(device_t dev)<br/>
{<br/>
&nbsp;&nbsp; &nbsp;int&nbsp;&nbsp; &nbsp;error;<br/>
&nbsp;&nbsp; &nbsp;ACPI_HANDLE&nbsp;&nbsp; &nbsp;handle;<br/>
&nbsp;&nbsp; &nbsp;ACPI_HANDLE tmp;<br/>
&nbsp;&nbsp; &nbsp;struct acpi_fan_softc *sc;</div>

<div>&nbsp;&nbsp; &nbsp;<br/>
&nbsp;&nbsp;&nbsp; sc = device_get_softc(dev);<br/>
&nbsp;&nbsp;&nbsp; handle = acpi_get_handle(dev);</div>

<div>&nbsp;</div>

<div>&nbsp;&nbsp; &nbsp;/* fans are either acpi 1.0 or 4.0 compatible, so check now. */<br/>
&nbsp;&nbsp; &nbsp;if (ACPI_SUCCESS(acpi_get_handle(handle, &quot;_FIF&quot;, &amp;tmp)) &amp;&amp;<br/>
&nbsp;&nbsp; &nbsp;ACPI_SUCCESS(acpi_get_handle(handle, &quot;_FPS&quot;, &amp;tmp)) &amp;&amp;<br/>
&nbsp;&nbsp; &nbsp;ACPI_SUCCESS(acpi_get_handle(handle, &quot;_FSL&quot;, &amp;tmp)) &amp;&amp;<br/>
&nbsp;&nbsp; &nbsp;ACPI_SUCCESS(acpi_get_handle(handle, &quot;_FST&quot;, &amp;tmp)))&nbsp;&nbsp; &nbsp;<br/>
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; acpi_fan_softc.version = 4;<br/>
&nbsp;&nbsp; &nbsp;<br/>
&nbsp;&nbsp; &nbsp;else&nbsp;&nbsp; &nbsp;/* nothing to do in acpi version 1, really */<br/>
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;acpi_fan_softc.version = 1;</div>

<div>&nbsp;</div>

<div>&nbsp; return 0;<br/>
}</div>

<div>&nbsp;</div>

<div>
<div>static int<br/>
acpi_fan_detach(device_t dev) {<br/>
&nbsp;&nbsp; &nbsp;sysctl_ctx_free(&amp;clist);<br/>
&nbsp;&nbsp; &nbsp;return 0;<br/>
}</div>

<div>&nbsp;</div>

<div>
<div><br/>
static driver_t acpi_fan_driver = {<br/>
&nbsp;&nbsp;&nbsp; &quot;fan&quot;,<br/>
&nbsp;&nbsp;&nbsp; acpi_fan_methods,<br/>
&nbsp;&nbsp;&nbsp; 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>&nbsp;</div>

<div>_____________________</div>

<div>5. How linux does it</div>

<div>&nbsp;</div>

<div>You can check linux fan driver on github:</div>

<div>https://github.com/torvalds/linux/tree/master/drivers/acpi</div>

<div>&nbsp;</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>&nbsp;</div>

<div>It&#39;s ok to learn from linux. :-)</div>

<div>&nbsp;</div>

<div>&nbsp;</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>&nbsp;Mittwoch, 03. Mai 2023 um 02:26 Uhr<br/>
<b>Von:</b>&nbsp;&quot;Dmitry N. Medvedev&quot; &lt;dmitry.medvedev@gmail.com&gt;<br/>
<b>An:</b>&nbsp;&quot;Adrian Chadd&quot; &lt;adrian@freebsd.org&gt;<br/>
<b>Cc:</b>&nbsp;freebsd-acpi@freebsd.org<br/>
<b>Betreff:</b>&nbsp;Re: managing a fan speed via memory address</div>

<div name="quoted-content">
<div>good morning Adrian,
<div>&nbsp;</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&nbsp;I will have to first find out how to do it :) any&nbsp;guidance ( preferable in textual form will be greatly appreciated ) will learn it :)</div>

<div>3. there isn&#39;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>
&nbsp;

<div class="gmail_quote">
<div class="gmail_attr">On Wed, May 3, 2023 at 2:14&#8239;AM Adrian Chadd &lt;<a href="mailto:adrian@freebsd.org" onclick="parent.window.location.href=&#39;mailto:adrian@freebsd.org&#39;; return false;" target="_blank">adrian@freebsd.org</a>&gt; 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>&nbsp;</div>

<div>Is there a thermal sensor(s) you could read to see how warm they get?</div>

<div>&nbsp;</div>

<div>&nbsp;</div>

<div>&nbsp;</div>

<div>-adrian</div>

<div>&nbsp;</div>
</div>
&nbsp;

<div class="gmail_quote">
<div class="gmail_attr">On Tue, 2 May 2023 at 17:06, Dmitry N. Medvedev &lt;<a href="mailto:dmitry.medvedev@gmail.com" onclick="parent.window.location.href=&#39;mailto:dmitry.medvedev@gmail.com&#39;; return false;" target="_blank">dmitry.medvedev@gmail.com</a>&gt; 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>&nbsp;</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>&nbsp;</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&nbsp;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>&nbsp;</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>