]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/powerpc/powermac/smu.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / powerpc / powermac / smu.c
1 /*-
2  * Copyright (c) 2009 Nathan Whitehorn
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
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/conf.h>
36 #include <sys/cpu.h>
37 #include <sys/ctype.h>
38 #include <sys/kernel.h>
39 #include <sys/kthread.h>
40 #include <sys/reboot.h>
41 #include <sys/rman.h>
42 #include <sys/sysctl.h>
43 #include <sys/unistd.h>
44
45 #include <machine/bus.h>
46 #include <machine/intr_machdep.h>
47 #include <machine/md_var.h>
48
49 #include <dev/led/led.h>
50 #include <dev/ofw/openfirm.h>
51 #include <dev/ofw/ofw_bus.h>
52 #include <powerpc/powermac/macgpiovar.h>
53
54 struct smu_cmd {
55         volatile uint8_t cmd;
56         uint8_t         len;
57         uint8_t         data[254];
58
59         STAILQ_ENTRY(smu_cmd) cmd_q;
60 };
61
62 STAILQ_HEAD(smu_cmdq, smu_cmd);
63
64 struct smu_fan {
65         cell_t  reg;
66         cell_t  min_rpm;
67         cell_t  max_rpm;
68         cell_t  unmanaged_rpm;
69         char    location[32];
70
71         int     old_style;
72         int     setpoint;
73 };
74
75 struct smu_sensor {
76         cell_t  reg;
77         char    location[32];
78         enum {
79                 SMU_CURRENT_SENSOR,
80                 SMU_VOLTAGE_SENSOR,
81                 SMU_POWER_SENSOR,
82                 SMU_TEMP_SENSOR
83         } type;
84 };
85
86 struct smu_softc {
87         device_t        sc_dev;
88         struct mtx      sc_mtx;
89
90         struct resource *sc_memr;
91         int             sc_memrid;
92
93         bus_dma_tag_t   sc_dmatag;
94         bus_space_tag_t sc_bt;
95         bus_space_handle_t sc_mailbox;
96
97         struct smu_cmd  *sc_cmd, *sc_cur_cmd;
98         bus_addr_t      sc_cmd_phys;
99         bus_dmamap_t    sc_cmd_dmamap;
100         struct smu_cmdq sc_cmdq;
101
102         struct smu_fan  *sc_fans;
103         int             sc_nfans;
104         struct smu_sensor *sc_sensors;
105         int             sc_nsensors;
106
107         int             sc_doorbellirqid;
108         struct resource *sc_doorbellirq;
109         void            *sc_doorbellirqcookie;
110
111         struct proc     *sc_fanmgt_proc;
112         time_t          sc_lastuserchange;
113
114         /* Calibration data */
115         uint16_t        sc_cpu_diode_scale;
116         int16_t         sc_cpu_diode_offset;
117
118         uint16_t        sc_cpu_volt_scale;
119         int16_t         sc_cpu_volt_offset;
120         uint16_t        sc_cpu_curr_scale;
121         int16_t         sc_cpu_curr_offset;
122
123         uint16_t        sc_slots_pow_scale;
124         int16_t         sc_slots_pow_offset;
125
126         /* Thermal management parameters */
127         int             sc_target_temp;         /* Default 55 C */
128         int             sc_critical_temp;       /* Default 90 C */
129
130         struct cdev     *sc_leddev;
131 };
132
133 /* regular bus attachment functions */
134
135 static int      smu_probe(device_t);
136 static int      smu_attach(device_t);
137
138 /* cpufreq notification hooks */
139
140 static void     smu_cpufreq_pre_change(device_t, const struct cf_level *level);
141 static void     smu_cpufreq_post_change(device_t, const struct cf_level *level);
142
143 /* utility functions */
144 static int      smu_run_cmd(device_t dev, struct smu_cmd *cmd, int wait);
145 static int      smu_get_datablock(device_t dev, int8_t id, uint8_t *buf,
146                     size_t len);
147 static void     smu_attach_fans(device_t dev, phandle_t fanroot);
148 static void     smu_attach_sensors(device_t dev, phandle_t sensroot);
149 static void     smu_fan_management_proc(void *xdev);
150 static void     smu_manage_fans(device_t smu);
151 static void     smu_set_sleepled(void *xdev, int onoff);
152 static int      smu_server_mode(SYSCTL_HANDLER_ARGS);
153 static void     smu_doorbell_intr(void *xdev);
154
155 /* where to find the doorbell GPIO */
156
157 static device_t smu_doorbell = NULL;
158
159 static device_method_t  smu_methods[] = {
160         /* Device interface */
161         DEVMETHOD(device_probe,         smu_probe),
162         DEVMETHOD(device_attach,        smu_attach),
163         { 0, 0 },
164 };
165
166 static driver_t smu_driver = {
167         "smu",
168         smu_methods,
169         sizeof(struct smu_softc)
170 };
171
172 static devclass_t smu_devclass;
173
174 DRIVER_MODULE(smu, nexus, smu_driver, smu_devclass, 0, 0);
175 MALLOC_DEFINE(M_SMU, "smu", "SMU Sensor Information");
176
177 #define SMU_MAILBOX             0x8000860c
178 #define SMU_FANMGT_INTERVAL     1000 /* ms */
179
180 /* Command types */
181 #define SMU_ADC                 0xd8
182 #define SMU_FAN                 0x4a
183 #define SMU_I2C                 0x9a
184 #define  SMU_I2C_SIMPLE         0x00
185 #define  SMU_I2C_NORMAL         0x01
186 #define  SMU_I2C_COMBINED       0x02
187 #define SMU_MISC                0xee
188 #define  SMU_MISC_GET_DATA      0x02
189 #define  SMU_MISC_LED_CTRL      0x04
190 #define SMU_POWER               0xaa
191 #define SMU_POWER_EVENTS        0x8f
192 #define  SMU_PWR_GET_POWERUP    0x00
193 #define  SMU_PWR_SET_POWERUP    0x01
194 #define  SMU_PWR_CLR_POWERUP    0x02
195
196 /* Power event types */
197 #define SMU_WAKEUP_KEYPRESS     0x01
198 #define SMU_WAKEUP_AC_INSERT    0x02
199 #define SMU_WAKEUP_AC_CHANGE    0x04
200 #define SMU_WAKEUP_RING         0x10
201
202 /* Data blocks */
203 #define SMU_CPUTEMP_CAL         0x18
204 #define SMU_CPUVOLT_CAL         0x21
205 #define SMU_SLOTPW_CAL          0x78
206
207 /* Partitions */
208 #define SMU_PARTITION           0x3e
209 #define SMU_PARTITION_LATEST    0x01
210 #define SMU_PARTITION_BASE      0x02
211 #define SMU_PARTITION_UPDATE    0x03
212
213 static int
214 smu_probe(device_t dev)
215 {
216         const char *name = ofw_bus_get_name(dev);
217
218         if (strcmp(name, "smu") != 0)
219                 return (ENXIO);
220
221         device_set_desc(dev, "Apple System Management Unit");
222         return (0);
223 }
224
225 static void
226 smu_phys_callback(void *xsc, bus_dma_segment_t *segs, int nsegs, int error)
227 {
228         struct smu_softc *sc = xsc;
229
230         sc->sc_cmd_phys = segs[0].ds_addr;
231 }
232
233 static int
234 smu_attach(device_t dev)
235 {
236         struct smu_softc *sc;
237         phandle_t       node, child;
238         uint8_t         data[12];
239
240         sc = device_get_softc(dev);
241
242         mtx_init(&sc->sc_mtx, "smu", NULL, MTX_DEF);
243         sc->sc_cur_cmd = NULL;
244         sc->sc_doorbellirqid = -1;
245
246         /*
247          * Map the mailbox area. This should be determined from firmware,
248          * but I have not found a simple way to do that.
249          */
250         bus_dma_tag_create(NULL, 16, 0, BUS_SPACE_MAXADDR_32BIT,
251             BUS_SPACE_MAXADDR, NULL, NULL, PAGE_SIZE, 1, PAGE_SIZE, 0, NULL,
252             NULL, &(sc->sc_dmatag));
253         sc->sc_bt = &bs_le_tag;
254         bus_space_map(sc->sc_bt, SMU_MAILBOX, 4, 0, &sc->sc_mailbox);
255
256         /*
257          * Allocate the command buffer. This can be anywhere in the low 4 GB
258          * of memory.
259          */
260         bus_dmamem_alloc(sc->sc_dmatag, (void **)&sc->sc_cmd, BUS_DMA_WAITOK | 
261             BUS_DMA_ZERO, &sc->sc_cmd_dmamap);
262         bus_dmamap_load(sc->sc_dmatag, sc->sc_cmd_dmamap,
263             sc->sc_cmd, PAGE_SIZE, smu_phys_callback, sc, 0);
264         STAILQ_INIT(&sc->sc_cmdq);
265
266         /*
267          * Set up handlers to change CPU voltage when CPU frequency is changed.
268          */
269         EVENTHANDLER_REGISTER(cpufreq_pre_change, smu_cpufreq_pre_change, dev,
270             EVENTHANDLER_PRI_ANY);
271         EVENTHANDLER_REGISTER(cpufreq_post_change, smu_cpufreq_post_change, dev,
272             EVENTHANDLER_PRI_ANY);
273
274         /*
275          * Detect and attach child devices.
276          */
277         node = ofw_bus_get_node(dev);
278         for (child = OF_child(node); child != 0; child = OF_peer(child)) {
279                 char name[32];
280                 memset(name, 0, sizeof(name));
281                 OF_getprop(child, "name", name, sizeof(name));
282
283                 if (strncmp(name, "rpm-fans", 9) == 0 ||
284                     strncmp(name, "fans", 5) == 0)
285                         smu_attach_fans(dev, child);
286
287                 if (strncmp(name, "sensors", 8) == 0)
288                         smu_attach_sensors(dev, child);
289         }
290
291         /*
292          * Collect calibration constants.
293          */
294         smu_get_datablock(dev, SMU_CPUTEMP_CAL, data, sizeof(data));
295         sc->sc_cpu_diode_scale = (data[4] << 8) + data[5];
296         sc->sc_cpu_diode_offset = (data[6] << 8) + data[7];
297
298         smu_get_datablock(dev, SMU_CPUVOLT_CAL, data, sizeof(data));
299         sc->sc_cpu_volt_scale = (data[4] << 8) + data[5];
300         sc->sc_cpu_volt_offset = (data[6] << 8) + data[7];
301         sc->sc_cpu_curr_scale = (data[8] << 8) + data[9];
302         sc->sc_cpu_curr_offset = (data[10] << 8) + data[11];
303
304         smu_get_datablock(dev, SMU_SLOTPW_CAL, data, sizeof(data));
305         sc->sc_slots_pow_scale = (data[4] << 8) + data[5];
306         sc->sc_slots_pow_offset = (data[6] << 8) + data[7];
307
308         /*
309          * Set up simple-minded thermal management.
310          */
311         sc->sc_target_temp = 55;
312         sc->sc_critical_temp = 90;
313
314         SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
315             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
316             "target_temp", CTLTYPE_INT | CTLFLAG_RW, &sc->sc_target_temp,
317             sizeof(int), "Target temperature (C)");
318         SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
319             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
320             "critical_temp", CTLTYPE_INT | CTLFLAG_RW,
321             &sc->sc_critical_temp, sizeof(int), "Critical temperature (C)");
322
323         kproc_create(smu_fan_management_proc, dev, &sc->sc_fanmgt_proc,
324             RFHIGHPID, 0, "smu_thermal");
325
326         /*
327          * Set up LED interface
328          */
329         sc->sc_leddev = led_create(smu_set_sleepled, dev, "sleepled");
330
331         /*
332          * Reset on power loss behavior
333          */
334
335         SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
336             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
337             "server_mode", CTLTYPE_INT | CTLFLAG_RW, dev, 0,
338             smu_server_mode, "I", "Enable reboot after power failure");
339
340         /*
341          * Set up doorbell interrupt.
342          */
343         sc->sc_doorbellirqid = 0;
344         sc->sc_doorbellirq = bus_alloc_resource_any(smu_doorbell, SYS_RES_IRQ,
345             &sc->sc_doorbellirqid, RF_ACTIVE);
346         bus_setup_intr(smu_doorbell, sc->sc_doorbellirq,
347             INTR_TYPE_MISC | INTR_MPSAFE, NULL, smu_doorbell_intr, dev,
348             &sc->sc_doorbellirqcookie);
349         powerpc_config_intr(rman_get_start(sc->sc_doorbellirq),
350             INTR_TRIGGER_EDGE, INTR_POLARITY_LOW);
351
352         return (0);
353 }
354
355 static void
356 smu_send_cmd(device_t dev, struct smu_cmd *cmd)
357 {
358         struct smu_softc *sc;
359
360         sc = device_get_softc(dev);
361
362         mtx_assert(&sc->sc_mtx, MA_OWNED);
363
364         powerpc_pow_enabled = 0;        /* SMU cannot work if we go to NAP */
365         sc->sc_cur_cmd = cmd;
366
367         /* Copy the command to the mailbox */
368         sc->sc_cmd->cmd = cmd->cmd;
369         sc->sc_cmd->len = cmd->len;
370         memcpy(sc->sc_cmd->data, cmd->data, sizeof(cmd->data));
371         bus_dmamap_sync(sc->sc_dmatag, sc->sc_cmd_dmamap, BUS_DMASYNC_PREWRITE);
372         bus_space_write_4(sc->sc_bt, sc->sc_mailbox, 0, sc->sc_cmd_phys);
373
374         /* Flush the cacheline it is in -- SMU bypasses the cache */
375         __asm __volatile("sync; dcbf 0,%0; sync" :: "r"(sc->sc_cmd): "memory");
376
377         /* Ring SMU doorbell */
378         macgpio_write(smu_doorbell, GPIO_DDR_OUTPUT);
379 }
380
381 static void
382 smu_doorbell_intr(void *xdev)
383 {
384         device_t smu;
385         struct smu_softc *sc;
386         int doorbell_ack;
387
388         smu = xdev;
389         doorbell_ack = macgpio_read(smu_doorbell);
390         sc = device_get_softc(smu);
391
392         if (doorbell_ack != (GPIO_DDR_OUTPUT | GPIO_LEVEL_RO | GPIO_DATA)) 
393                 return;
394
395         mtx_lock(&sc->sc_mtx);
396
397         if (sc->sc_cur_cmd == NULL)     /* spurious */
398                 goto done;
399
400         /* Check result. First invalidate the cache again... */
401         __asm __volatile("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory");
402         
403         bus_dmamap_sync(sc->sc_dmatag, sc->sc_cmd_dmamap, BUS_DMASYNC_POSTREAD);
404
405         sc->sc_cur_cmd->cmd = sc->sc_cmd->cmd;
406         sc->sc_cur_cmd->len = sc->sc_cmd->len;
407         memcpy(sc->sc_cur_cmd->data, sc->sc_cmd->data,
408             sizeof(sc->sc_cmd->data));
409         wakeup(sc->sc_cur_cmd);
410         sc->sc_cur_cmd = NULL;
411         powerpc_pow_enabled = 1;
412
413     done:
414         /* Queue next command if one is pending */
415         if (STAILQ_FIRST(&sc->sc_cmdq) != NULL) {
416                 sc->sc_cur_cmd = STAILQ_FIRST(&sc->sc_cmdq);
417                 STAILQ_REMOVE_HEAD(&sc->sc_cmdq, cmd_q);
418                 smu_send_cmd(smu, sc->sc_cur_cmd);
419         }
420
421         mtx_unlock(&sc->sc_mtx);
422 }
423
424 static int
425 smu_run_cmd(device_t dev, struct smu_cmd *cmd, int wait)
426 {
427         struct smu_softc *sc;
428         uint8_t cmd_code;
429         int error;
430
431         sc = device_get_softc(dev);
432         cmd_code = cmd->cmd;
433
434         mtx_lock(&sc->sc_mtx);
435         if (sc->sc_cur_cmd != NULL) {
436                 STAILQ_INSERT_TAIL(&sc->sc_cmdq, cmd, cmd_q);
437         } else
438                 smu_send_cmd(dev, cmd);
439         mtx_unlock(&sc->sc_mtx);
440
441         if (!wait)
442                 return (0);
443
444         if (sc->sc_doorbellirqid < 0) {
445                 /* Poll if the IRQ has not been set up yet */
446                 do {
447                         DELAY(50);
448                         smu_doorbell_intr(dev);
449                 } while (sc->sc_cur_cmd != NULL);
450         } else {
451                 /* smu_doorbell_intr will wake us when the command is ACK'ed */
452                 error = tsleep(cmd, 0, "smu", 800 * hz / 1000);
453                 if (error != 0)
454                         smu_doorbell_intr(dev); /* One last chance */
455                 
456                 if (error != 0) {
457                     mtx_lock(&sc->sc_mtx);
458                     if (cmd->cmd == cmd_code) { /* Never processed */
459                         /* Abort this command if we timed out */
460                         if (sc->sc_cur_cmd == cmd)
461                                 sc->sc_cur_cmd = NULL;
462                         else
463                                 STAILQ_REMOVE(&sc->sc_cmdq, cmd, smu_cmd,
464                                     cmd_q);
465                         mtx_unlock(&sc->sc_mtx);
466                         return (error);
467                     }
468                     error = 0;
469                     mtx_unlock(&sc->sc_mtx);
470                 }
471         }
472
473         /* SMU acks the command by inverting the command bits */
474         if (cmd->cmd == ((~cmd_code) & 0xff))
475                 error = 0;
476         else
477                 error = EIO;
478
479         return (error);
480 }
481
482 static int
483 smu_get_datablock(device_t dev, int8_t id, uint8_t *buf, size_t len)
484 {
485         struct smu_cmd cmd;
486         uint8_t addr[4];
487
488         cmd.cmd = SMU_PARTITION;
489         cmd.len = 2;
490         cmd.data[0] = SMU_PARTITION_LATEST;
491         cmd.data[1] = id; 
492
493         smu_run_cmd(dev, &cmd, 1);
494
495         addr[0] = addr[1] = 0;
496         addr[2] = cmd.data[0];
497         addr[3] = cmd.data[1];
498
499         cmd.cmd = SMU_MISC;
500         cmd.len = 7;
501         cmd.data[0] = SMU_MISC_GET_DATA;
502         cmd.data[1] = sizeof(addr);
503         memcpy(&cmd.data[2], addr, sizeof(addr));
504         cmd.data[6] = len;
505
506         smu_run_cmd(dev, &cmd, 1);
507         memcpy(buf, cmd.data, len);
508         return (0);
509 }
510
511 static void
512 smu_slew_cpu_voltage(device_t dev, int to)
513 {
514         struct smu_cmd cmd;
515
516         cmd.cmd = SMU_POWER;
517         cmd.len = 8;
518         cmd.data[0] = 'V';
519         cmd.data[1] = 'S'; 
520         cmd.data[2] = 'L'; 
521         cmd.data[3] = 'E'; 
522         cmd.data[4] = 'W'; 
523         cmd.data[5] = 0xff;
524         cmd.data[6] = 1;
525         cmd.data[7] = to;
526
527         smu_run_cmd(dev, &cmd, 1);
528 }
529
530 static void
531 smu_cpufreq_pre_change(device_t dev, const struct cf_level *level)
532 {
533         /*
534          * Make sure the CPU voltage is raised before we raise
535          * the clock.
536          */
537                 
538         if (level->rel_set[0].freq == 10000 /* max */)
539                 smu_slew_cpu_voltage(dev, 0);
540 }
541
542 static void
543 smu_cpufreq_post_change(device_t dev, const struct cf_level *level)
544 {
545         /* We are safe to reduce CPU voltage after a downward transition */
546
547         if (level->rel_set[0].freq < 10000 /* max */)
548                 smu_slew_cpu_voltage(dev, 1); /* XXX: 1/4 voltage for 970MP? */
549 }
550
551 /* Routines for probing the SMU doorbell GPIO */
552 static int doorbell_probe(device_t dev);
553 static int doorbell_attach(device_t dev);
554
555 static device_method_t  doorbell_methods[] = {
556         /* Device interface */
557         DEVMETHOD(device_probe,         doorbell_probe),
558         DEVMETHOD(device_attach,        doorbell_attach),
559         { 0, 0 },
560 };
561
562 static driver_t doorbell_driver = {
563         "smudoorbell",
564         doorbell_methods,
565         0
566 };
567
568 static devclass_t doorbell_devclass;
569
570 DRIVER_MODULE(smudoorbell, macgpio, doorbell_driver, doorbell_devclass, 0, 0);
571
572 static int
573 doorbell_probe(device_t dev)
574 {
575         const char *name = ofw_bus_get_name(dev);
576
577         if (strcmp(name, "smu-doorbell") != 0)
578                 return (ENXIO);
579
580         device_set_desc(dev, "SMU Doorbell GPIO");
581         device_quiet(dev);
582         return (0);
583 }
584
585 static int
586 doorbell_attach(device_t dev)
587 {
588         smu_doorbell = dev;
589         return (0);
590 }
591
592 /*
593  * Sensor and fan management
594  */
595
596 static int
597 smu_fan_set_rpm(device_t smu, struct smu_fan *fan, int rpm)
598 {
599         struct smu_cmd cmd;
600         int error;
601
602         cmd.cmd = SMU_FAN;
603         error = EIO;
604
605         /* Clamp to allowed range */
606         rpm = max(fan->min_rpm, rpm);
607         rpm = min(fan->max_rpm, rpm);
608
609         /*
610          * Apple has two fan control mechanisms. We can't distinguish
611          * them except by seeing if the new one fails. If the new one
612          * fails, use the old one.
613          */
614         
615         if (!fan->old_style) {
616                 cmd.len = 4;
617                 cmd.data[0] = 0x30;
618                 cmd.data[1] = fan->reg;
619                 cmd.data[2] = (rpm >> 8) & 0xff;
620                 cmd.data[3] = rpm & 0xff;
621         
622                 error = smu_run_cmd(smu, &cmd, 1);
623                 if (error)
624                         fan->old_style = 1;
625         }
626
627         if (fan->old_style) {
628                 cmd.len = 14;
629                 cmd.data[0] = 0;
630                 cmd.data[1] = 1 << fan->reg;
631                 cmd.data[2 + 2*fan->reg] = (rpm >> 8) & 0xff;
632                 cmd.data[3 + 2*fan->reg] = rpm & 0xff;
633                 error = smu_run_cmd(smu, &cmd, 1);
634         }
635
636         if (error == 0)
637                 fan->setpoint = rpm;
638
639         return (error);
640 }
641
642 static int
643 smu_fan_read_rpm(device_t smu, struct smu_fan *fan)
644 {
645         struct smu_cmd cmd;
646         int rpm, error;
647
648         if (!fan->old_style) {
649                 cmd.cmd = SMU_FAN;
650                 cmd.len = 2;
651                 cmd.data[0] = 0x31;
652                 cmd.data[1] = fan->reg;
653
654                 error = smu_run_cmd(smu, &cmd, 1);
655                 if (error)
656                         fan->old_style = 1;
657
658                 rpm = (cmd.data[0] << 8) | cmd.data[1];
659         }
660
661         if (fan->old_style) {
662                 cmd.cmd = SMU_FAN;
663                 cmd.len = 1;
664                 cmd.data[0] = 1;
665
666                 error = smu_run_cmd(smu, &cmd, 1);
667                 if (error)
668                         return (error);
669
670                 rpm = (cmd.data[fan->reg*2+1] << 8) | cmd.data[fan->reg*2+2];
671         }
672
673         return (rpm);
674 }
675
676 static int
677 smu_fanrpm_sysctl(SYSCTL_HANDLER_ARGS)
678 {
679         device_t smu;
680         struct smu_softc *sc;
681         struct smu_fan *fan;
682         int rpm, error;
683
684         smu = arg1;
685         sc = device_get_softc(smu);
686         fan = &sc->sc_fans[arg2];
687
688         rpm = smu_fan_read_rpm(smu, fan);
689         if (rpm < 0)
690                 return (rpm);
691
692         error = sysctl_handle_int(oidp, &rpm, 0, req);
693
694         if (error || !req->newptr)
695                 return (error);
696
697         sc->sc_lastuserchange = time_uptime;
698
699         return (smu_fan_set_rpm(smu, fan, rpm));
700 }
701
702 static void
703 smu_attach_fans(device_t dev, phandle_t fanroot)
704 {
705         struct smu_fan *fan;
706         struct smu_softc *sc;
707         struct sysctl_oid *oid, *fanroot_oid;
708         struct sysctl_ctx_list *ctx;
709         phandle_t child;
710         char type[32], sysctl_name[32];
711         int i;
712
713         sc = device_get_softc(dev);
714         sc->sc_nfans = 0;
715
716         for (child = OF_child(fanroot); child != 0; child = OF_peer(child))
717                 sc->sc_nfans++;
718
719         if (sc->sc_nfans == 0) {
720                 device_printf(dev, "WARNING: No fans detected!\n");
721                 return;
722         }
723
724         sc->sc_fans = malloc(sc->sc_nfans * sizeof(struct smu_fan), M_SMU,
725             M_WAITOK | M_ZERO);
726
727         fan = sc->sc_fans;
728         sc->sc_nfans = 0;
729
730         ctx = device_get_sysctl_ctx(dev);
731         fanroot_oid = SYSCTL_ADD_NODE(ctx,
732             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "fans",
733             CTLFLAG_RD, 0, "SMU Fan Information");
734
735         for (child = OF_child(fanroot); child != 0; child = OF_peer(child)) {
736                 OF_getprop(child, "device_type", type, sizeof(type));
737                 if (strcmp(type, "fan-rpm-control") != 0)
738                         continue;
739
740                 fan->old_style = 0;
741                 OF_getprop(child, "reg", &fan->reg, sizeof(cell_t));
742                 OF_getprop(child, "min-value", &fan->min_rpm, sizeof(cell_t));
743                 OF_getprop(child, "max-value", &fan->max_rpm, sizeof(cell_t));
744
745                 if (OF_getprop(child, "unmanaged-value", &fan->unmanaged_rpm,
746                     sizeof(cell_t)) != sizeof(cell_t))
747                         fan->unmanaged_rpm = fan->max_rpm;
748
749                 fan->setpoint = smu_fan_read_rpm(dev, fan);
750
751                 OF_getprop(child, "location", fan->location,
752                     sizeof(fan->location));
753         
754                 /* Add sysctls */
755                 for (i = 0; i < strlen(fan->location); i++) {
756                         sysctl_name[i] = tolower(fan->location[i]);
757                         if (isspace(sysctl_name[i]))
758                                 sysctl_name[i] = '_';
759                 }
760                 sysctl_name[i] = 0;
761
762                 oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(fanroot_oid),
763                     OID_AUTO, sysctl_name, CTLFLAG_RD, 0, "Fan Information");
764                 SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "minrpm",
765                     CTLTYPE_INT | CTLFLAG_RD, &fan->min_rpm, sizeof(cell_t),
766                     "Minimum allowed RPM");
767                 SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "maxrpm",
768                     CTLTYPE_INT | CTLFLAG_RD, &fan->max_rpm, sizeof(cell_t),
769                     "Maximum allowed RPM");
770                 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "rpm",
771                     CTLTYPE_INT | CTLFLAG_RW, dev, sc->sc_nfans,
772                     smu_fanrpm_sysctl, "I", "Fan RPM");
773
774                 fan++;
775                 sc->sc_nfans++;
776         }
777 }
778
779 static int
780 smu_sensor_read(device_t smu, struct smu_sensor *sens, int *val)
781 {
782         struct smu_cmd cmd;
783         struct smu_softc *sc;
784         int64_t value;
785         int error;
786
787         cmd.cmd = SMU_ADC;
788         cmd.len = 1;
789         cmd.data[0] = sens->reg;
790         error = 0;
791
792         error = smu_run_cmd(smu, &cmd, 1);
793         if (error != 0)
794                 return (error);
795         
796         sc = device_get_softc(smu);
797         value = (cmd.data[0] << 8) | cmd.data[1];
798
799         switch (sens->type) {
800         case SMU_TEMP_SENSOR:
801                 value *= sc->sc_cpu_diode_scale;
802                 value >>= 3;
803                 value += ((int64_t)sc->sc_cpu_diode_offset) << 9;
804                 value <<= 1;
805
806                 /* Convert from 16.16 fixed point degC into integer C. */
807                 value >>= 16;
808                 break;
809         case SMU_VOLTAGE_SENSOR:
810                 value *= sc->sc_cpu_volt_scale;
811                 value += sc->sc_cpu_volt_offset;
812                 value <<= 4;
813
814                 /* Convert from 16.16 fixed point V into mV. */
815                 value *= 15625;
816                 value /= 1024;
817                 value /= 1000;
818                 break;
819         case SMU_CURRENT_SENSOR:
820                 value *= sc->sc_cpu_curr_scale;
821                 value += sc->sc_cpu_curr_offset;
822                 value <<= 4;
823
824                 /* Convert from 16.16 fixed point A into mA. */
825                 value *= 15625;
826                 value /= 1024;
827                 value /= 1000;
828                 break;
829         case SMU_POWER_SENSOR:
830                 value *= sc->sc_slots_pow_scale;
831                 value += sc->sc_slots_pow_offset;
832                 value <<= 4;
833
834                 /* Convert from 16.16 fixed point W into mW. */
835                 value *= 15625;
836                 value /= 1024;
837                 value /= 1000;
838                 break;
839         }
840
841         *val = value;
842         return (0);
843 }
844
845 static int
846 smu_sensor_sysctl(SYSCTL_HANDLER_ARGS)
847 {
848         device_t smu;
849         struct smu_softc *sc;
850         struct smu_sensor *sens;
851         int value, error;
852
853         smu = arg1;
854         sc = device_get_softc(smu);
855         sens = &sc->sc_sensors[arg2];
856
857         error = smu_sensor_read(smu, sens, &value);
858         if (error != 0)
859                 return (error);
860
861         error = sysctl_handle_int(oidp, &value, 0, req);
862
863         return (error);
864 }
865
866 static void
867 smu_attach_sensors(device_t dev, phandle_t sensroot)
868 {
869         struct smu_sensor *sens;
870         struct smu_softc *sc;
871         struct sysctl_oid *sensroot_oid;
872         struct sysctl_ctx_list *ctx;
873         phandle_t child;
874         char type[32];
875         int i;
876
877         sc = device_get_softc(dev);
878         sc->sc_nsensors = 0;
879
880         for (child = OF_child(sensroot); child != 0; child = OF_peer(child))
881                 sc->sc_nsensors++;
882
883         if (sc->sc_nsensors == 0) {
884                 device_printf(dev, "WARNING: No sensors detected!\n");
885                 return;
886         }
887
888         sc->sc_sensors = malloc(sc->sc_nsensors * sizeof(struct smu_sensor),
889             M_SMU, M_WAITOK | M_ZERO);
890
891         sens = sc->sc_sensors;
892         sc->sc_nsensors = 0;
893
894         ctx = device_get_sysctl_ctx(dev);
895         sensroot_oid = SYSCTL_ADD_NODE(ctx,
896             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensors",
897             CTLFLAG_RD, 0, "SMU Sensor Information");
898
899         for (child = OF_child(sensroot); child != 0; child = OF_peer(child)) {
900                 char sysctl_name[40], sysctl_desc[40];
901                 const char *units;
902
903                 OF_getprop(child, "device_type", type, sizeof(type));
904
905                 if (strcmp(type, "current-sensor") == 0) {
906                         sens->type = SMU_CURRENT_SENSOR;
907                         units = "mA";
908                 } else if (strcmp(type, "temp-sensor") == 0) {
909                         sens->type = SMU_TEMP_SENSOR;
910                         units = "C";
911                 } else if (strcmp(type, "voltage-sensor") == 0) {
912                         sens->type = SMU_VOLTAGE_SENSOR;
913                         units = "mV";
914                 } else if (strcmp(type, "power-sensor") == 0) {
915                         sens->type = SMU_POWER_SENSOR;
916                         units = "mW";
917                 } else {
918                         continue;
919                 }
920
921                 OF_getprop(child, "reg", &sens->reg, sizeof(cell_t));
922                 OF_getprop(child, "location", sens->location,
923                     sizeof(sens->location));
924
925                 for (i = 0; i < strlen(sens->location); i++) {
926                         sysctl_name[i] = tolower(sens->location[i]);
927                         if (isspace(sysctl_name[i]))
928                                 sysctl_name[i] = '_';
929                 }
930                 sysctl_name[i] = 0;
931
932                 sprintf(sysctl_desc,"%s (%s)", sens->location, units);
933
934                 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sensroot_oid), OID_AUTO,
935                     sysctl_name, CTLTYPE_INT | CTLFLAG_RD, dev, sc->sc_nsensors,
936                     smu_sensor_sysctl, "I", sysctl_desc);
937
938                 sens++;
939                 sc->sc_nsensors++;
940         }
941 }
942
943 static void
944 smu_fan_management_proc(void *xdev)
945 {
946         device_t smu = xdev;
947
948         while(1) {
949                 smu_manage_fans(smu);
950                 pause("smu", SMU_FANMGT_INTERVAL * hz / 1000);
951         }
952 }
953
954 static void
955 smu_manage_fans(device_t smu)
956 {
957         struct smu_softc *sc;
958         int i, maxtemp, temp, factor, error;
959
960         sc = device_get_softc(smu);
961
962         maxtemp = 0;
963         for (i = 0; i < sc->sc_nsensors; i++) {
964                 if (sc->sc_sensors[i].type != SMU_TEMP_SENSOR)
965                         continue;
966
967                 error = smu_sensor_read(smu, &sc->sc_sensors[i], &temp);
968                 if (error == 0 && temp > maxtemp)
969                         maxtemp = temp;
970         }
971
972         if (maxtemp < 10) { /* Bail if no good sensors */
973                 for (i = 0; i < sc->sc_nfans; i++) 
974                         smu_fan_set_rpm(smu, &sc->sc_fans[i],
975                             sc->sc_fans[i].unmanaged_rpm);
976                 return;
977         }
978
979         if (maxtemp > sc->sc_critical_temp) {
980                 device_printf(smu, "WARNING: Current system temperature (%d C) "
981                     "exceeds critical temperature (%d C)! Shutting down!\n",
982                     maxtemp, sc->sc_critical_temp);
983                 shutdown_nice(RB_POWEROFF);
984         }
985
986         if (maxtemp - sc->sc_target_temp > 20)
987                 device_printf(smu, "WARNING: Current system temperature (%d C) "
988                     "more than 20 degrees over target temperature (%d C)!\n",
989                     maxtemp, sc->sc_target_temp);
990
991         if (time_uptime - sc->sc_lastuserchange < 3) {
992                 /*
993                  * If we have heard from a user process in the last 3 seconds,
994                  * go away.
995                  */
996
997                 return;
998         }
999
1000         if (maxtemp - sc->sc_target_temp > 4) 
1001                 factor = 110;
1002         else if (maxtemp - sc->sc_target_temp > 1) 
1003                 factor = 105;
1004         else if (sc->sc_target_temp - maxtemp > 4) 
1005                 factor = 90;
1006         else if (sc->sc_target_temp - maxtemp > 1) 
1007                 factor = 95;
1008         else
1009                 factor = 100;
1010
1011         for (i = 0; i < sc->sc_nfans; i++) 
1012                 smu_fan_set_rpm(smu, &sc->sc_fans[i],
1013                     (sc->sc_fans[i].setpoint * factor) / 100);
1014 }
1015
1016 static void
1017 smu_set_sleepled(void *xdev, int onoff)
1018 {
1019         static struct smu_cmd cmd;
1020         device_t smu = xdev;
1021
1022         cmd.cmd = SMU_MISC;
1023         cmd.len = 3;
1024         cmd.data[0] = SMU_MISC_LED_CTRL;
1025         cmd.data[1] = 0;
1026         cmd.data[2] = onoff; 
1027
1028         smu_run_cmd(smu, &cmd, 0);
1029 }
1030
1031 static int
1032 smu_server_mode(SYSCTL_HANDLER_ARGS)
1033 {
1034         struct smu_cmd cmd;
1035         u_int server_mode;
1036         device_t smu = arg1;
1037         int error;
1038         
1039         cmd.cmd = SMU_POWER_EVENTS;
1040         cmd.len = 1;
1041         cmd.data[0] = SMU_PWR_GET_POWERUP;
1042
1043         error = smu_run_cmd(smu, &cmd, 1);
1044
1045         if (error)
1046                 return (error);
1047
1048         server_mode = (cmd.data[1] & SMU_WAKEUP_AC_INSERT) ? 1 : 0;
1049
1050         error = sysctl_handle_int(oidp, &server_mode, 0, req);
1051
1052         if (error || !req->newptr)
1053                 return (error);
1054
1055         if (server_mode == 1)
1056                 cmd.data[0] = SMU_PWR_SET_POWERUP;
1057         else if (server_mode == 0)
1058                 cmd.data[0] = SMU_PWR_CLR_POWERUP;
1059         else
1060                 return (EINVAL);
1061
1062         cmd.len = 3;
1063         cmd.data[1] = 0;
1064         cmd.data[2] = SMU_WAKEUP_AC_INSERT;
1065
1066         return (smu_run_cmd(smu, &cmd, 1));
1067 }
1068