1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
|
.\" $NetBSD: emcfanctl.8,v 1.2 2025/03/12 00:43:28 uwe Exp $
.\"
.\" Copyright (c) 2025 Brad Spencer <brad@anduin.eldar.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd Feburary 20, 2025
.Dt EMCFANCTL 8
.Os
.Sh NAME
.Nm emcfanctl
.Nd Command line utility to interact with EMC fan controllers
.Sh SYNOPSIS
.
.Nm
.Op Fl dhj
.Ar device
.Cm info
.
.Pp
.Nm
.Op Fl dhj
.Ar device
.Cm register list
.
.Nm
.Op Fl dhj
.Ar device
.Cm register read Ar start_register Op Ar end_register
.
.Nm
.Op Fl dhj
.Ar device
.Cm register write Ar register value
.
.Pp
.Nm
.Op Fl dhj
.Ar device
.Cm fan Ar n Cm status
.
.Nm
.Op Fl dhj
.Ar device
.Cm fan Ar n Cm drive read
.
.Nm
.Op Fl dhj
.Ar device
.Cm fan Ar n Cm drive write value
.
.Nm
.Op Fl dhj
.Ar device
.Cm fan Ar n Cm divider read
.
.Nm
.Op Fl dhj
.Ar device
.Cm fan Ar n Cm divider write value
.
.Nm
.Op Fl dhj
.Ar device
.Cm fan Ar n Cm min_expected_rpm read
.
.Nm
.Op Fl dhj
.Ar device
.Cm fan Ar n Cm min_expected_rpm write Li 500|1000|2000|4000
.
.Nm
.Op Fl dhj
.Ar device
.Cm fan Ar n Cm edges read
.
.Nm
.Op Fl dhj
.Ar device
.Cm fan Ar n Cm edges write Li 3|5|7|9
.
.Nm
.Op Fl dhj
.Ar device
.Cm fan Ar n Cm polarity read
.
.Nm
.Op Fl dhj
.Ar device
.Cm fan Ar n Cm polarity inverted
.
.Nm
.Op Fl dhj
.Ar device
.Cm fan Ar n Cm polarity non-inverted
.
.Nm
.Op Fl dhj
.Ar device
.Cm fan Ar n Cm pwm_base_frequency read
.
.Nm
.Op Fl dhj
.Ar device
.Cm fan Ar n Cm pwm_base_frequency write Li 26000|19531|4882|2441
.
.Nm
.Op Fl dhj
.Ar device
.Cm fan Ar n Cm pwm_output_type read
.
.Nm
.Op Fl dhj
.Ar device
.Cm fan Ar n Cm pwm_output_type push-pull
.
.Nm
.Op Fl dhj
.Ar device
.Cm fan Ar n Cm pwm_output_type open-drain
.
.Pp
.Nm
.Op Fl dhj
.Ar device
.Cm apd read
.
.Nm
.Op Fl dhj
.Ar device
.Cm apd on
.
.Nm
.Op Fl dhj
.Ar device
.Cm apd off
.
.Pp
.Nm
.Op Fl dhj
.Ar device
.Cm smbus_timeout read
.
.Nm
.Op Fl dhj
.Ar device
.Cm smbus_timeout on
.
.Nm
.Op Fl dhj
.Ar device
.Cm smbus_timeout off
.
.Sh DESCRIPTION
The
.Nm
utility interacts with a Microchip Technology EMC-210x or EMC-230x fan controller via
.Xr emcfan 4
driver.
.Pp
The options are as follows:
.Bl -tag -width Fl
.It Fl d
Debug mode.
.It Fl h
Display help.
.It Fl j
For the commands that produce output, output the result in JSON.
.El
.Pp
The commands are as follows:
.
.Bl -tag -width Cm
.It Cm info
Print the family, chip id and chip revision for the specific
.Ar device .
.It Cm register list
Print the valid registers for the particular chip at the specific
.Ar device .
.It Cm register read Ar start_register Op Ar end_register
Print the values present in the range of registers from
.Ar start_register
to
.Ar end_register .
If
.Ar end_register
is missing, just print one register at
.Ar start_register .
It is possible to use the text names given out by the
.Cm list
command for
.Ar start_register
or
.Ar end_register .
.It Cm register write Ar a_register value
Write
.Ar value
into the register called
.Ar a_register .
.El
.
.Pp
The EMC210X and EMC230X fan controllers have a tremendous number of
features and options and can run in number of different modes.
What follows are some short cut commands that can be used for some of
the more common things one might want to do with a particular
controller.
.Pp \" XXX: compact hack alert
.Bl -tag -width Cm -compact
.It Cm fan Ar n Cm status
Print the stall, spin up and drive status for a particular fan.
Note that the fan will be marked as stalled if the RPMs are below the
minumum expected RPM level.
.Pp
.It Cm fan Ar n Cm drive read
Print the current value of the drive level for a particular fan.
.Pp
.It Cm fan Ar n Cm drive write value
Set the drive level for a particular fan to value.
.Pp
.It Cm fan Ar n Cm divider read
Print the current value of the frequency divider for a particular fan.
.Pp
.It Cm fan Ar n Cm drive write value
Set the frequency divider for a particular fan to value.
.Pp
.It Cm fan Ar n Cm drive min_expected_rpm read
Print the current minimum expected RPM that a fan is suppose to run
at.
If the RPMs are lower than the expected value, the fan will be
marked as stalled and the RPM value in
.Xr envstat 8
will be
.Ql N/A .
.Pp
.It Cm fan Ar n Cm drive min_expected_rpm write Li 500|1000|2000|4000
Set the minimum expected RPM value for a fan.
.Pp
.It Cm fan Ar n Cm drive edges read
Print the number of edges that a particular fan has.
This value, along with hw.emcfan0.poles, is used in the tachometer
algorithm to determine the RPM.
.Pp
.It Cm fan Ar n Cm drive edges write Li 3|5|7|9
Set the number of edges that the fan has.
.Pp
.It Cm fan Ar n Cm polarity read
.It Cm fan Ar n Cm polarity inverted
.It Cm fan Ar n Cm polarity non-inverted
Print or set the polarity of the drive level for the fan.
When the polarity is inverted a drive level of 0 will be maximum
drive, and when the polarity is non-inverted, a drive level of 0 is
minimum drive.
.Pp
.It Cm fan Ar n Cm drive pwm_base_frequency read
Print the number of PWM base frequency for a particular fan.
.Pp
.It Cm fan Ar n Cm drive pwm_base_frequency write Li 26000|19531|4882|2441
Set the base PWM frequency for a particular fan.
.Pp
.It Cm fan Ar n Cm pwm_output_type read
.It Cm fan Ar n Cm pwm_output_type push-pull
.It Cm fan Ar n Cm pwm_output_type open-drain
Print or set the PWM output type for a particular fan.
.Pp
.It Cm apd read
.It Cm apd on
.It Cm apd off
Print, turn on or turn off the anti-parallel diode mode on the chip.
The EMC2103-2/4, EMC2104 and EMC2106 have the ability to connect two
temperature sensor diodes together with just two wires.
In order to be able to read both diodes, APD needs to be turned on.
.Pp
.It Cm smbus_timeout read
.It Cm smbus_timeout on
.It Cm smbus_timeout off
Print, turn on or turn off
.Tn SMBUS
timeout.
.Tn I2C
and
.Tn SMBUS
are very simular, but a difference is that
.Tn SMBUS
clients can trigger a bus timeout if operations are not performed
against the chip in a certain amount of time.
In order to be completely
.Tn I2C
compliant, the
.Tn SMBUS
timeout should be turned off.
Some of the EMC product default this to on and some default it to off.
.El
.Pp
Not all of the above options apply to all chip types and the
.Nm
command will error if the option does not apply to a particular device.
.
.Sh EXAMPLES
.
This will print the chip family and product id for a particular device:
.Bd -literal -offset indent
# emcfanctl /dev/emcfan0 info
Product Family: EMC230x
Chip name: EMC2301
Revision: 1
.Ed
.
.Pp
This is the same, except in JSON:
.Bd -literal -offset indent
# emcfanctl -j /dev/emcfan0 info | json_pp
{
"chip_name" : "EMC2301",
"family_name" : "EMC230x",
"product_family" : 2,
"product_id" : 55,
"revision" : 1
}
.Ed
.Pp
This reads a number of registers from the chip and output the result
in a JSON array:
.Bd -literal -offset indent
# emcfanctl -j /dev/emcfan0 register read 0x20 0x29 | json_pp
[
{
"register" : 32,
"register_name" : "configuration",
"register_value" : 64
},
{
"register" : 36,
"register_name" : "fan_status",
"register_value" : 0
},
{
"register" : 37,
"register_name" : "fan_stall_status",
"register_value" : 0
},
{
"register" : 38,
"register_name" : "fan_spin_status",
"register_value" : 0
},
{
"register" : 39,
"register_name" : "drive_fail_status",
"register_value" : 0
},
{
"register" : 41,
"register_name" : "fan_interrupt_enable_register",
"register_value" : 0
}
]
.Ed
.Pp
You can use names for the registers.
The following produces the same result as the previous example, except
not in JSON:
.Bd -literal -offset indent
# emcfanctl /dev/emcfan0 register read configuration drive_fail_status
configuration;32 (0x20);64 (0x40)
fan_status;36 (0x24);0 (0x00)
fan_stall_status;37 (0x25);0 (0x00)
fan_spin_status;38 (0x26);0 (0x00)
drive_fail_status;39 (0x27);0 (0x00)
.Ed
.Pp
This writes a
.Vt uint8_t
value to a particular register:
.Pp
.Dl "# emcfanctl /dev/emcfan0 register write configuration 0xc0"
.Pp
This read back the 0x20 register, also known as
.Ql configuration
as a JSON array.
Using the
.Xr jq 1
command the value is extracted.
.Bd -literal -offset indent
# emcfanctl -j /dev/emcfan0 register read 0x20 | jq -r '.[0].register_value'
192
.Ed
.Pp
Read the current drive level for fan #1 on a particular device:
.Bd -literal -offset indent
# emcfanctl /dev/emcfan0 fan 1 drive read
Drive:96
.Ed
.Pp
Change the drive level for fan #1.
A number of other variables such as polarity and the PWM divider
affect what the drive level means.
.Bd -literal -offset indent
# emcfanctl /dev/emcfan0 fan 1 drive write 0x80
# emcfanctl /dev/emcfan0 fan 1 drive read
Drive:128
.Ed
.Pp
If the
.Xr envstat 8
command is used to look at the RPM of a fan, it will produce something
like the following:
.Bd -literal -offset indent
Current CritMax WarnMax WarnMin CritMin Unit
[emcfan0]
FAN 1: 1159 RPM
.Ed
.Pp
This is below the minumum expected RPM that the fan is suppose to run at:
.Bd -literal -offset indent
# emcfanctl /dev/emcfan0 fan 1 min_expected_rpm read
Minumum expected rpm:500
.Ed
.Pp
If the minimum expected RPM is changed to be higher than what the fan
is able to run at, that will simulate a stalled fan.
.Pp
.Dl "# emcfanctl /dev/emcfan0 fan 1 min_expected rpm write 4000"
.Pp
Using the
.Xr envstat 8
command again should produce the following if the fan is not able to
run at 4000\~RPM:
.Bd -literal -offset indent
Current CritMax WarnMax WarnMin CritMin Unit
[emcfan0]
FAN 1: N/A
.Ed
.Pp
The fan will be marked as having stalled:
.Bd -literal -offset indent
# emcfanctl /dev/emcfan0 fan 1 status
Stalled: Yes
Spin up failed: No
Drive failed: No
.Ed
.Pp
The minimum expected RPM should be set to just below the lowest value
that the fan is expected to run at.
The minumum expected RPM effects the accuracy of the tachometers and
should be as high as it can be made while still producing usable RPM
values.
.Pp
.Dl "# emcfanctl /dev/emcfan0 fan 1 min_expected rpm write 500"
.Pp
Using the
.Xr envstat 8
command again:
.Bd -literal -offset indent
Current CritMax WarnMax WarnMin CritMin Unit
[emcfan0]
FAN 1: 1176 RPM
.Ed
.Pp
The fan is not marked as having stalled:
.Bd -literal -offset indent
# emcfanctl /dev/emcfan0 fan 1 status
Stalled: No
Spin up failed: No
Drive failed: No
.Ed
.Sh SEE ALSO
.Xr emcfan 4 ,
.Xr iic 4 ,
.Xr envstat 8
.Sh HISTORY
The
.Nm
utility first appeared in
.Nx 11.0 .
.Sh AUTHORS
.An -nosplit
The
.Nm
utility was written by
.An Brad Spencer Aq Mt brad@anduin.eldar.org .
|