]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/powerpc/powermac/fcu.c
Drop 'All rights reserved'
[FreeBSD/FreeBSD.git] / sys / powerpc / powermac / fcu.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2010 Andreas Tobler
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/param.h>
32 #include <sys/bus.h>
33 #include <sys/systm.h>
34 #include <sys/module.h>
35 #include <sys/callout.h>
36 #include <sys/conf.h>
37 #include <sys/cpu.h>
38 #include <sys/ctype.h>
39 #include <sys/kernel.h>
40 #include <sys/reboot.h>
41 #include <sys/rman.h>
42 #include <sys/sysctl.h>
43 #include <sys/limits.h>
44
45 #include <machine/bus.h>
46 #include <machine/md_var.h>
47
48 #include <dev/iicbus/iicbus.h>
49 #include <dev/iicbus/iiconf.h>
50
51 #include <dev/ofw/openfirm.h>
52 #include <dev/ofw/ofw_bus.h>
53 #include <powerpc/powermac/powermac_thermal.h>
54
55 /* FCU registers
56  * /u3@0,f8000000/i2c@f8001000/fan@15e
57  */
58 #define FCU_RPM_FAIL      0x0b      /* fans states in bits 0<1-6>7 */
59 #define FCU_RPM_AVAILABLE 0x0c
60 #define FCU_RPM_ACTIVE    0x0d
61 #define FCU_RPM_READ(x)   0x11 + (x) * 2
62 #define FCU_RPM_SET(x)    0x10 + (x) * 2
63
64 #define FCU_PWM_FAIL      0x2b
65 #define FCU_PWM_AVAILABLE 0x2c
66 #define FCU_PWM_ACTIVE    0x2d
67 #define FCU_PWM_RPM(x)    0x31 + (x) * 2 /* Get RPM. */
68 #define FCU_PWM_SGET(x)   0x30 + (x) * 2 /* Set or get PWM. */
69
70 struct fcu_fan {
71         struct  pmac_fan fan;
72         device_t dev;
73
74         int     id;
75         enum {
76                 FCU_FAN_RPM,
77                 FCU_FAN_PWM
78         } type;
79         int     setpoint;
80         int     rpm;
81 };
82
83 struct fcu_softc {
84         device_t                sc_dev;
85         struct intr_config_hook enum_hook;
86         uint32_t                sc_addr;
87         struct fcu_fan          *sc_fans;
88         int                     sc_nfans;
89 };
90
91 /* We can read the PWM and the RPM from a PWM controlled fan.
92  * Offer both values via sysctl.
93  */
94 enum {
95         FCU_PWM_SYSCTL_PWM   = 1 << 8,
96         FCU_PWM_SYSCTL_RPM   = 2 << 8
97 };
98
99 static int fcu_rpm_shift;
100
101 /* Regular bus attachment functions */
102 static int  fcu_probe(device_t);
103 static int  fcu_attach(device_t);
104
105 /* Utility functions */
106 static void fcu_attach_fans(device_t dev);
107 static int  fcu_fill_fan_prop(device_t dev);
108 static int  fcu_fan_set_rpm(struct fcu_fan *fan, int rpm);
109 static int  fcu_fan_get_rpm(struct fcu_fan *fan);
110 static int  fcu_fan_set_pwm(struct fcu_fan *fan, int pwm);
111 static int  fcu_fan_get_pwm(device_t dev, struct fcu_fan *fan, int *pwm,
112                             int *rpm);
113 static int  fcu_fanrpm_sysctl(SYSCTL_HANDLER_ARGS);
114 static void fcu_start(void *xdev);
115 static int  fcu_write(device_t dev, uint32_t addr, uint8_t reg, uint8_t *buf,
116                       int len);
117 static int  fcu_read_1(device_t dev, uint32_t addr, uint8_t reg, uint8_t *data);
118
119 static device_method_t  fcu_methods[] = {
120         /* Device interface */
121         DEVMETHOD(device_probe,         fcu_probe),
122         DEVMETHOD(device_attach,        fcu_attach),
123         { 0, 0 },
124 };
125
126 static driver_t fcu_driver = {
127         "fcu",
128         fcu_methods,
129         sizeof(struct fcu_softc)
130 };
131
132 static devclass_t fcu_devclass;
133
134 DRIVER_MODULE(fcu, iicbus, fcu_driver, fcu_devclass, 0, 0);
135 static MALLOC_DEFINE(M_FCU, "fcu", "FCU Sensor Information");
136
137 static int
138 fcu_write(device_t dev, uint32_t addr, uint8_t reg, uint8_t *buff,
139           int len)
140 {
141         unsigned char buf[4];
142         int try = 0;
143
144         struct iic_msg msg[] = {
145                 { addr, IIC_M_WR, 0, buf }
146         };
147
148         msg[0].len = len + 1;
149         buf[0] = reg;
150         memcpy(buf + 1, buff, len);
151
152         for (;;)
153         {
154                 if (iicbus_transfer(dev, msg, nitems(msg)) == 0)
155                         return (0);
156
157                 if (++try > 5) {
158                         device_printf(dev, "iicbus write failed\n");
159                         return (-1);
160                 }
161                 pause("fcu_write", hz);
162         }
163 }
164
165 static int
166 fcu_read_1(device_t dev, uint32_t addr, uint8_t reg, uint8_t *data)
167 {
168         uint8_t buf[4];
169         int err, try = 0;
170
171         struct iic_msg msg[2] = {
172             { addr, IIC_M_WR | IIC_M_NOSTOP, 1, &reg },
173             { addr, IIC_M_RD, 1, buf },
174         };
175
176         for (;;)
177         {
178                   err = iicbus_transfer(dev, msg, nitems(msg));
179                   if (err != 0)
180                           goto retry;
181
182                   *data = *((uint8_t*)buf);
183                   return (0);
184         retry:
185                   if (++try > 5) {
186                           device_printf(dev, "iicbus read failed\n");
187                           return (-1);
188                   }
189                   pause("fcu_read_1", hz);
190         }
191 }
192
193 static int
194 fcu_probe(device_t dev)
195 {
196         const char  *name, *compatible;
197         struct fcu_softc *sc;
198
199         name = ofw_bus_get_name(dev);
200         compatible = ofw_bus_get_compat(dev);
201
202         if (!name)
203                 return (ENXIO);
204
205         if (strcmp(name, "fan") != 0 || strcmp(compatible, "fcu") != 0)
206                 return (ENXIO);
207
208         sc = device_get_softc(dev);
209         sc->sc_dev = dev;
210         sc->sc_addr = iicbus_get_addr(dev);
211
212         device_set_desc(dev, "Apple Fan Control Unit");
213
214         return (0);
215 }
216
217 static int
218 fcu_attach(device_t dev)
219 {
220         struct fcu_softc *sc;
221
222         sc = device_get_softc(dev);
223
224         sc->enum_hook.ich_func = fcu_start;
225         sc->enum_hook.ich_arg = dev;
226
227         /* We have to wait until interrupts are enabled. I2C read and write
228          * only works if the interrupts are available.
229          * The unin/i2c is controlled by the htpic on unin. But this is not
230          * the master. The openpic on mac-io is controlling the htpic.
231          * This one gets attached after the mac-io probing and then the
232          * interrupts will be available.
233          */
234
235         if (config_intrhook_establish(&sc->enum_hook) != 0)
236                 return (ENOMEM);
237
238         return (0);
239 }
240
241 static void
242 fcu_start(void *xdev)
243 {
244         unsigned char buf[1] = { 0xff };
245         struct fcu_softc *sc;
246
247         device_t dev = (device_t)xdev;
248
249         sc = device_get_softc(dev);
250
251         /* Start the fcu device. */
252         fcu_write(sc->sc_dev, sc->sc_addr, 0xe, buf, sizeof(buf));
253         fcu_write(sc->sc_dev, sc->sc_addr, 0x2e, buf, sizeof(buf));
254         fcu_read_1(sc->sc_dev, sc->sc_addr, 0, buf);
255         fcu_rpm_shift = (buf[0] == 1) ? 2 : 3;
256
257         device_printf(dev, "FCU initialized, RPM shift: %d\n",
258                       fcu_rpm_shift);
259
260         /* Detect and attach child devices. */
261
262         fcu_attach_fans(dev);
263
264         config_intrhook_disestablish(&sc->enum_hook);
265
266 }
267
268 static int
269 fcu_fan_set_rpm(struct fcu_fan *fan, int rpm)
270 {
271         uint8_t reg;
272         struct fcu_softc *sc;
273         unsigned char buf[2];
274
275         sc = device_get_softc(fan->dev);
276
277         /* Clamp to allowed range */
278         rpm = max(fan->fan.min_rpm, rpm);
279         rpm = min(fan->fan.max_rpm, rpm);
280
281         if (fan->type == FCU_FAN_RPM) {
282                 reg = FCU_RPM_SET(fan->id);
283                 fan->setpoint = rpm;
284         } else {
285                 device_printf(fan->dev, "Unknown fan type: %d\n", fan->type);
286                 return (ENXIO);
287         }
288
289         buf[0] = rpm >> (8 - fcu_rpm_shift);
290         buf[1] = rpm << fcu_rpm_shift;
291
292         if (fcu_write(sc->sc_dev, sc->sc_addr, reg, buf, sizeof(buf)) < 0)
293                 return (EIO);
294
295         return (0);
296 }
297
298 static int
299 fcu_fan_get_rpm(struct fcu_fan *fan)
300 {
301         uint8_t reg;
302         struct fcu_softc *sc;
303         uint8_t buff[2] = { 0, 0 };
304         uint8_t active = 0, avail = 0, fail = 0;
305         int rpm;
306
307         sc = device_get_softc(fan->dev);
308
309         if (fan->type == FCU_FAN_RPM) {
310                 /* Check if the fan is available. */
311                 reg = FCU_RPM_AVAILABLE;
312                 if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &avail) < 0)
313                         return (-1);
314                 if ((avail & (1 << fan->id)) == 0) {
315                         device_printf(fan->dev,
316                             "RPM Fan not available ID: %d\n", fan->id);
317                         return (-1);
318                 }
319                 /* Check if we have a failed fan. */
320                 reg = FCU_RPM_FAIL;
321                 if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &fail) < 0)
322                         return (-1);
323                 if ((fail & (1 << fan->id)) != 0) {
324                         device_printf(fan->dev,
325                             "RPM Fan failed ID: %d %#x\n", fan->id, fail);
326                         return (-1);
327                 }
328                 /* Check if fan is active. */
329                 reg = FCU_RPM_ACTIVE;
330                 if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &active) < 0)
331                         return (-1);
332                 if ((active & (1 << fan->id)) == 0) {
333                         device_printf(fan->dev, "RPM Fan not active ID: %d\n",
334                                       fan->id);
335                         return (-1);
336                 }
337                 reg = FCU_RPM_READ(fan->id);
338
339         } else {
340                 device_printf(fan->dev, "Unknown fan type: %d\n", fan->type);
341                 return (-1);
342         }
343
344         /* It seems that we can read the fans rpm. */
345         if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, buff) < 0)
346                 return (-1);
347
348         rpm = (buff[0] << (8 - fcu_rpm_shift)) | buff[1] >> fcu_rpm_shift;
349
350         return (rpm);
351 }
352
353 static int
354 fcu_fan_set_pwm(struct fcu_fan *fan, int pwm)
355 {
356         uint8_t reg;
357         struct fcu_softc *sc;
358         uint8_t buf[1];
359
360         sc = device_get_softc(fan->dev);
361
362         /* Clamp to allowed range */
363         pwm = max(fan->fan.min_rpm, pwm);
364         pwm = min(fan->fan.max_rpm, pwm);
365
366         if (fan->type == FCU_FAN_PWM) {
367                 reg = FCU_PWM_SGET(fan->id);
368                 if (pwm > 100)
369                         pwm = 100;
370                 if (pwm < 30)
371                         pwm = 30;
372                 fan->setpoint = pwm;
373         } else {
374                 device_printf(fan->dev, "Unknown fan type: %d\n", fan->type);
375                 return (EIO);
376         }
377
378         buf[0] = (pwm * 2550) / 1000;
379
380         if (fcu_write(sc->sc_dev, sc->sc_addr, reg, buf, sizeof(buf)) < 0)
381                 return (EIO);
382         return (0);
383 }
384
385 static int
386 fcu_fan_get_pwm(device_t dev, struct fcu_fan *fan, int *pwm, int *rpm)
387 {
388         uint8_t reg;
389         struct fcu_softc *sc;
390         uint8_t buf[2];
391         uint8_t active = 0, avail = 0, fail = 0;
392
393         sc = device_get_softc(dev);
394
395         if (fan->type == FCU_FAN_PWM) {
396                 /* Check if the fan is available. */
397                 reg = FCU_PWM_AVAILABLE;
398                 if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &avail) < 0)
399                         return (-1);
400                 if ((avail & (1 << fan->id)) == 0) {
401                         device_printf(dev, "PWM Fan not available ID: %d\n",
402                                       fan->id);
403                         return (-1);
404                 }
405                 /* Check if we have a failed fan. */
406                 reg = FCU_PWM_FAIL;
407                 if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &fail) < 0)
408                         return (-1);
409                 if ((fail & (1 << fan->id)) != 0) {
410                         device_printf(dev, "PWM Fan failed ID: %d\n", fan->id);
411                         return (-1);
412                 }
413                 /* Check if fan is active. */
414                 reg = FCU_PWM_ACTIVE;
415                 if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &active) < 0)
416                         return (-1);
417                 if ((active & (1 << fan->id)) == 0) {
418                         device_printf(dev, "PWM Fan not active ID: %d\n",
419                                       fan->id);
420                         return (-1);
421                 }
422                 reg = FCU_PWM_SGET(fan->id);
423         } else {
424                 device_printf(dev, "Unknown fan type: %d\n", fan->type);
425                 return (EIO);
426         }
427
428         /* It seems that we can read the fans pwm. */
429         if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, buf) < 0)
430                 return (-1);
431
432         *pwm = (buf[0] * 1000) / 2550;
433
434         /* Now read the rpm. */
435         reg = FCU_PWM_RPM(fan->id);
436         if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, buf) < 0)
437                 return (-1);
438
439         *rpm = (buf[0] << (8 - fcu_rpm_shift)) | buf[1] >> fcu_rpm_shift;
440
441         return (0);
442 }
443
444 /*
445  * This function returns the number of fans. If we call it the second time
446  * and we have allocated memory for sc->sc_fans, we fill in the properties.
447  */
448 static int
449 fcu_fill_fan_prop(device_t dev)
450 {
451         phandle_t child;
452         struct fcu_softc *sc;
453         u_int id[12];
454         char location[144];
455         char type[96];
456         int i = 0, j, len = 0, prop_len, prev_len = 0;
457
458         sc = device_get_softc(dev);
459
460         child = ofw_bus_get_node(dev);
461
462         /* Fill the fan location property. */
463         prop_len = OF_getprop(child, "hwctrl-location", location,
464                               sizeof(location));
465         while (len < prop_len) {
466                 if (sc->sc_fans != NULL) {
467                         strcpy(sc->sc_fans[i].fan.name, location + len);
468                 }
469                 prev_len = strlen(location + len) + 1;
470                 len += prev_len;
471                 i++;
472         }
473         if (sc->sc_fans == NULL)
474                 return (i);
475
476         /* Fill the fan type property. */
477         len = 0;
478         i = 0;
479         prev_len = 0;
480         prop_len = OF_getprop(child, "hwctrl-type", type, sizeof(type));
481         while (len < prop_len) {
482                 if (strcmp(type + len, "fan-rpm") == 0)
483                         sc->sc_fans[i].type = FCU_FAN_RPM;
484                 else
485                         sc->sc_fans[i].type = FCU_FAN_PWM;
486                 prev_len = strlen(type + len) + 1;
487                 len += prev_len;
488                 i++;
489         }
490
491         /* Fill the fan ID property. */
492         prop_len = OF_getprop(child, "hwctrl-id", id, sizeof(id));
493         for (j = 0; j < i; j++)
494                 sc->sc_fans[j].id = ((id[j] >> 8) & 0x0f) % 8;
495
496         /* Fill the fan zone property. */
497         prop_len = OF_getprop(child, "hwctrl-zone", id, sizeof(id));
498         for (j = 0; j < i; j++)
499                 sc->sc_fans[j].fan.zone = id[j];
500
501         /* Finish setting up fan properties */
502         for (j = 0; j < i; j++) {
503                 sc->sc_fans[j].dev = sc->sc_dev;
504                 if (sc->sc_fans[j].type == FCU_FAN_RPM) {
505                         sc->sc_fans[j].fan.min_rpm = 4800 >> fcu_rpm_shift;
506                         sc->sc_fans[j].fan.max_rpm = 56000 >> fcu_rpm_shift;
507                         sc->sc_fans[j].setpoint =
508                             fcu_fan_get_rpm(&sc->sc_fans[j]);
509                         sc->sc_fans[j].fan.read = 
510                             (int (*)(struct pmac_fan *))(fcu_fan_get_rpm);
511                         sc->sc_fans[j].fan.set =
512                             (int (*)(struct pmac_fan *, int))(fcu_fan_set_rpm);
513                 } else {
514                         sc->sc_fans[j].fan.min_rpm = 30;        /* Percent */
515                         sc->sc_fans[j].fan.max_rpm = 100;
516                         sc->sc_fans[j].fan.read = NULL;
517                         sc->sc_fans[j].fan.set =
518                             (int (*)(struct pmac_fan *, int))(fcu_fan_set_pwm);
519                 }
520                 sc->sc_fans[j].fan.default_rpm = sc->sc_fans[j].fan.max_rpm;
521         }
522
523         return (i);
524 }
525
526 static int
527 fcu_fanrpm_sysctl(SYSCTL_HANDLER_ARGS)
528 {
529         device_t fcu;
530         struct fcu_softc *sc;
531         struct fcu_fan *fan;
532         int rpm = 0, pwm = 0, error = 0;
533
534         fcu = arg1;
535         sc = device_get_softc(fcu);
536         fan = &sc->sc_fans[arg2 & 0x00ff];
537         if (fan->type == FCU_FAN_RPM) {
538                 rpm = fcu_fan_get_rpm(fan);
539                 if (rpm < 0)
540                         return (EIO);
541                 error = sysctl_handle_int(oidp, &rpm, 0, req);
542         } else {
543                 error = fcu_fan_get_pwm(fcu, fan, &pwm, &rpm);
544                 if (error < 0)
545                         return (EIO);
546
547                 switch (arg2 & 0xff00) {
548                 case FCU_PWM_SYSCTL_PWM:
549                         error = sysctl_handle_int(oidp, &pwm, 0, req);
550                         break;
551                 case FCU_PWM_SYSCTL_RPM:
552                         error = sysctl_handle_int(oidp, &rpm, 0, req);
553                         break;
554                 default:
555                         /* This should never happen */
556                         return (EINVAL);
557                 }
558         }
559
560         /* We can only read the RPM from a PWM controlled fan, so return. */
561         if ((arg2 & 0xff00) == FCU_PWM_SYSCTL_RPM)
562                 return (0);
563
564         if (error || !req->newptr)
565                 return (error);
566
567         if (fan->type == FCU_FAN_RPM)
568                 return (fcu_fan_set_rpm(fan, rpm));
569         else
570                 return (fcu_fan_set_pwm(fan, pwm));
571 }
572
573 static void
574 fcu_attach_fans(device_t dev)
575 {
576         struct fcu_softc *sc;
577         struct sysctl_oid *oid, *fanroot_oid;
578         struct sysctl_ctx_list *ctx;
579         char sysctl_name[32];
580         int i, j;
581
582         sc = device_get_softc(dev);
583
584         sc->sc_nfans = 0;
585
586         /* Count the actual number of fans. */
587         sc->sc_nfans = fcu_fill_fan_prop(dev);
588
589         device_printf(dev, "%d fans detected!\n", sc->sc_nfans);
590
591         if (sc->sc_nfans == 0) {
592                 device_printf(dev, "WARNING: No fans detected!\n");
593                 return;
594         }
595
596         sc->sc_fans = malloc(sc->sc_nfans * sizeof(struct fcu_fan), M_FCU,
597                              M_WAITOK | M_ZERO);
598
599         ctx = device_get_sysctl_ctx(dev);
600         fanroot_oid = SYSCTL_ADD_NODE(ctx,
601             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "fans",
602             CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "FCU Fan Information");
603
604         /* Now we can fill the properties into the allocated struct. */
605         sc->sc_nfans = fcu_fill_fan_prop(dev);
606
607         /* Register fans with pmac_thermal */
608         for (i = 0; i < sc->sc_nfans; i++)
609                 pmac_thermal_fan_register(&sc->sc_fans[i].fan);
610
611         /* Add sysctls for the fans. */
612         for (i = 0; i < sc->sc_nfans; i++) {
613                 for (j = 0; j < strlen(sc->sc_fans[i].fan.name); j++) {
614                         sysctl_name[j] = tolower(sc->sc_fans[i].fan.name[j]);
615                         if (isspace(sysctl_name[j]))
616                                 sysctl_name[j] = '_';
617                 }
618                 sysctl_name[j] = 0;
619
620                 if (sc->sc_fans[i].type == FCU_FAN_RPM) {
621                         oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(fanroot_oid),
622                             OID_AUTO, sysctl_name, CTLFLAG_RD | CTLFLAG_MPSAFE,
623                             0, "Fan Information");
624                         SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
625                                        "minrpm", CTLFLAG_RD,
626                                        &(sc->sc_fans[i].fan.min_rpm), 0,
627                                        "Minimum allowed RPM");
628                         SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
629                                        "maxrpm", CTLFLAG_RD,
630                                        &(sc->sc_fans[i].fan.max_rpm), 0,
631                                        "Maximum allowed RPM");
632                         /* I use i to pass the fan id. */
633                         SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
634                                         "rpm", CTLTYPE_INT | CTLFLAG_RW |
635                                         CTLFLAG_MPSAFE, dev, i,
636                                         fcu_fanrpm_sysctl, "I", "Fan RPM");
637                 } else {
638                         fcu_fan_get_pwm(dev, &sc->sc_fans[i],
639                                         &sc->sc_fans[i].setpoint,
640                                         &sc->sc_fans[i].rpm);
641
642                         oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(fanroot_oid),
643                             OID_AUTO, sysctl_name, CTLFLAG_RD | CTLFLAG_MPSAFE,
644                             0, "Fan Information");
645                         SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
646                                        "minpwm", CTLFLAG_RD,
647                                        &(sc->sc_fans[i].fan.min_rpm), 0,
648                                        "Minimum allowed PWM in %");
649                         SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
650                                        "maxpwm", CTLFLAG_RD,
651                                        &(sc->sc_fans[i].fan.max_rpm), 0,
652                                        "Maximum allowed PWM in %");
653                         /* I use i to pass the fan id or'ed with the type
654                          * of info I want to display/modify.
655                          */
656                         SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
657                                         "pwm", CTLTYPE_INT | CTLFLAG_RW |
658                                         CTLFLAG_MPSAFE, dev,
659                                         FCU_PWM_SYSCTL_PWM | i,
660                                         fcu_fanrpm_sysctl, "I", "Fan PWM in %");
661                                         "rpm", CTLTYPE_INT | CTLFLAG_RD |
662                                         CTLFLAG_MPSAFE, dev,
663                                         FCU_PWM_SYSCTL_RPM | i,
664                                         fcu_fanrpm_sysctl, "I", "Fan RPM");
665                 }
666         }
667
668         /* Dump fan location, type & RPM. */
669         if (bootverbose) {
670                 device_printf(dev, "Fans\n");
671                 for (i = 0; i < sc->sc_nfans; i++) {
672                         device_printf(dev, "Location: %s type: %d ID: %d "
673                                       "RPM: %d\n", sc->sc_fans[i].fan.name,
674                                       sc->sc_fans[i].type, sc->sc_fans[i].id,
675                                       (sc->sc_fans[i].type == FCU_FAN_RPM) ?
676                                       sc->sc_fans[i].setpoint :
677                                       sc->sc_fans[i].rpm );
678             }
679         }
680 }