smu.c revision 1.1 1 /*-
2 * Copyright (c) 2013 Phileas Fogg
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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
15 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
16 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include <sys/param.h>
28 #include <sys/systm.h>
29 #include <sys/kernel.h>
30 #include <sys/malloc.h>
31 #include <sys/device.h>
32 #include <sys/proc.h>
33 #include <sys/mutex.h>
34 #include <sys/time.h>
35 #include <sys/reboot.h>
36 #include <sys/sysctl.h>
37
38 #include <machine/autoconf.h>
39
40 #include <dev/ofw/openfirm.h>
41 #include <dev/i2c/i2cvar.h>
42 #include <dev/clock_subr.h>
43 #include <dev/sysmon/sysmonvar.h>
44 #include <dev/sysmon/sysmon_taskq.h>
45
46 #include <macppc/dev/obiovar.h>
47 #include <macppc/dev/smuvar.h>
48
49 #include "opt_smu.h"
50
51 struct smu_softc;
52
53 struct smu_cmd {
54 u_char cmd;
55 u_char len;
56 u_char data[254];
57 };
58
59 struct smu_fan {
60 struct smu_softc* sc;
61
62 char location[32];
63 int reg;
64 int zone;
65 int rpm_ctl;
66 int min_rpm;
67 int max_rpm;
68 int default_rpm;
69 int current_rpm;
70 time_t last_update;
71 };
72
73 struct smu_iicbus {
74 struct smu_softc* sc;
75
76 int reg;
77 struct i2c_controller i2c;
78 };
79
80 #define SMU_MAX_FANS 8
81 #define SMU_MAX_IICBUS 3
82 #define SMU_MAX_SME_SENSORS SMU_MAX_FANS
83
84 struct smu_softc {
85 device_t sc_dev;
86 int sc_node;
87 struct sysctlnode *sc_sysctl_me;
88
89 kmutex_t sc_cmd_lock;
90 struct smu_cmd *sc_cmd;
91 paddr_t sc_cmd_paddr;
92 int sc_dbell_mbox;
93 int sc_dbell_gpio;
94
95 int sc_num_fans;
96 struct smu_fan sc_fans[SMU_MAX_FANS];
97
98 kmutex_t sc_iicbus_lock;
99 int sc_num_iicbus;
100 struct smu_iicbus sc_iicbus[SMU_MAX_IICBUS];
101
102 struct todr_chip_handle sc_todr;
103
104 struct sysmon_envsys *sc_sme;
105 envsys_data_t sc_sme_sensors[SMU_MAX_SME_SENSORS];
106 };
107
108 #define SMU_CMD_FAN 0x4a
109 #define SMU_CMD_RTC 0x8e
110 #define SMU_CMD_I2C 0x9a
111 #define SMU_CMD_POWER 0xaa
112
113 #ifdef SMU_DEBUG
114 #define DPRINTF printf
115 #else
116 #define DPRINTF while (0) printf
117 #endif
118
119 static int smu_match(device_t, struct cfdata *, void *);
120 static void smu_attach(device_t, device_t, void *);
121 static int smu_setup_doorbell(struct smu_softc *);
122 static void smu_setup_fans(struct smu_softc *);
123 static void smu_setup_iicbus(struct smu_softc *);
124 static void smu_setup_sme(struct smu_softc *);
125 static int smu_iicbus_print(void *, const char *);
126 static void smu_sme_refresh(struct sysmon_envsys *, envsys_data_t *);
127 static int smu_do_cmd(struct smu_softc *, struct smu_cmd *, int);
128 static int smu_dbell_gpio_intr(void *);
129 static int smu_todr_gettime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *);
130 static int smu_todr_settime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *);
131 static int smu_fan_update_rpm(struct smu_fan *);
132 static int smu_fan_get_rpm(struct smu_fan *, int *);
133 static int smu_fan_set_rpm(struct smu_fan *, int);
134 static int smu_iicbus_acquire_bus(void *, int);
135 static void smu_iicbus_release_bus(void *, int);
136 static int smu_iicbus_exec(void *, i2c_op_t, i2c_addr_t, const void *,
137 size_t, void *, size_t, int);
138 static int smu_sysctl_fan_rpm(SYSCTLFN_ARGS);
139
140 CFATTACH_DECL_NEW(smu, sizeof(struct smu_softc),
141 smu_match, smu_attach, NULL, NULL);
142
143 static struct smu_softc *smu0 = NULL;
144
145 static int
146 smu_match(device_t parent, struct cfdata *cf, void *aux)
147 {
148 struct confargs *ca = aux;
149
150 if (strcmp(ca->ca_name, "smu") == 0)
151 return 5;
152
153 return 0;
154 }
155
156 static void
157 smu_attach(device_t parent, device_t self, void *aux)
158 {
159 struct confargs *ca = aux;
160 struct smu_softc *sc = device_private(self);
161
162 sc->sc_dev = self;
163 sc->sc_node = ca->ca_node;
164
165 sysctl_createv(NULL, 0, NULL, (void *) &sc->sc_sysctl_me,
166 CTLFLAG_READWRITE,
167 CTLTYPE_NODE, device_xname(sc->sc_dev), NULL,
168 NULL, 0, NULL, 0,
169 CTL_MACHDEP, CTL_CREATE, CTL_EOL);
170
171 if (smu_setup_doorbell(sc) != 0) {
172 aprint_normal(": unable to set up doorbell\n");
173 return;
174 }
175
176 smu_setup_fans(sc);
177 smu_setup_iicbus(sc);
178
179 sc->sc_todr.todr_gettime_ymdhms = smu_todr_gettime_ymdhms;
180 sc->sc_todr.todr_settime_ymdhms = smu_todr_settime_ymdhms;
181 sc->sc_todr.cookie = sc;
182 todr_attach(&sc->sc_todr);
183
184 smu_setup_sme(sc);
185
186 if (smu0 == NULL)
187 smu0 = sc;
188
189 printf("\n");
190 }
191
192 static int
193 smu_setup_doorbell(struct smu_softc *sc)
194 {
195 int node, parent, reg[4], gpio_base, irq;
196
197 mutex_init(&sc->sc_cmd_lock, MUTEX_DEFAULT, IPL_NONE);
198 sc->sc_cmd = malloc(4096, M_DEVBUF, M_NOWAIT);
199 sc->sc_cmd_paddr = vtophys((vaddr_t) sc->sc_cmd);
200
201 DPRINTF("%s: cmd vaddr 0x%x paddr 0x%x\n",
202 __func__, (unsigned int) sc->sc_cmd,
203 (unsigned int) sc->sc_cmd_paddr);
204
205 if (OF_getprop(sc->sc_node, "platform-doorbell-buff",
206 &node, sizeof(node)) <= 0)
207 return -1;
208
209 if (OF_getprop(node, "platform-do-doorbell-buff",
210 reg, sizeof(reg)) < sizeof(reg))
211 return -1;
212
213 sc->sc_dbell_mbox = reg[3];
214
215 if (OF_getprop(sc->sc_node, "platform-doorbell-ack",
216 &node, sizeof(node)) <= 0)
217 return -1;
218
219 parent = OF_parent(node);
220 if (parent == 0)
221 return -1;
222
223 if (OF_getprop(parent, "reg", &gpio_base, sizeof(gpio_base)) <= 0)
224 return -1;
225
226 if (OF_getprop(node, "reg", reg, sizeof(reg)) <= 0)
227 return -1;
228
229 if (OF_getprop(node, "interrupts", &irq, sizeof(irq)) <= 0)
230 return -1;
231
232 sc->sc_dbell_gpio = gpio_base + reg[0];
233
234 aprint_normal(" mbox 0x%x gpio 0x%x irq %d",
235 sc->sc_dbell_mbox, sc->sc_dbell_gpio, irq);
236
237 intr_establish(irq, IST_EDGE_FALLING, IPL_TTY, smu_dbell_gpio_intr, sc);
238
239 return 0;
240 }
241
242 static void
243 smu_setup_fans(struct smu_softc *sc)
244 {
245 struct smu_fan *fan;
246 struct sysctlnode *sysctl_fans, *sysctl_fan, *sysctl_node;
247 char type[32], sysctl_fan_name[32];
248 int node, i, j;
249
250 node = of_getnode_byname(sc->sc_node, "fans");
251 for (node = OF_child(node);
252 (node != 0) && (sc->sc_num_fans < SMU_MAX_FANS);
253 node = OF_peer(node)) {
254 fan = &sc->sc_fans[sc->sc_num_fans];
255 fan->sc = sc;
256
257 memset(fan->location, 0, sizeof(fan->location));
258 OF_getprop(node, "location", fan->location,
259 sizeof(fan->location));
260
261 if (OF_getprop(node, "reg", &fan->reg,
262 sizeof(fan->reg)) <= 0)
263 continue;
264
265 if (OF_getprop(node, "zone", &fan->zone,
266 sizeof(fan->zone)) <= 0)
267 continue;
268
269 memset(type, 0, sizeof(type));
270 OF_getprop(node, "device_type", type, sizeof(type));
271 if (strcmp(type, "fan-rpm-control") == 0)
272 fan->rpm_ctl = 1;
273 else
274 fan->rpm_ctl = 0;
275
276 if (OF_getprop(node, "min-value", &fan->min_rpm,
277 sizeof(fan->min_rpm)) <= 0)
278 fan->min_rpm = 0;
279
280 if (OF_getprop(node, "max-value", &fan->max_rpm,
281 sizeof(fan->max_rpm)) <= 0)
282 fan->max_rpm = 0xffff;
283
284 if (OF_getprop(node, "unmanage-value", &fan->default_rpm,
285 sizeof(fan->default_rpm)) <= 0)
286 fan->default_rpm = fan->max_rpm;
287
288 DPRINTF("fan: location %s reg %x zone %d rpm_ctl %d "
289 "min_rpm %d max_rpm %d default_rpm %d\n",
290 fan->location, fan->reg, fan->zone, fan->rpm_ctl,
291 fan->min_rpm, fan->max_rpm, fan->default_rpm);
292
293 sc->sc_num_fans++;
294 }
295
296 for (i = 0; i < sc->sc_num_fans; i++) {
297 fan = &sc->sc_fans[i];
298 smu_fan_set_rpm(fan, fan->default_rpm);
299 smu_fan_get_rpm(fan, &fan->current_rpm);
300 }
301
302 /* Create sysctl nodes for each fan */
303
304 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_fans,
305 CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
306 CTLTYPE_NODE, "fans", NULL,
307 NULL, 0, NULL, 0,
308 CTL_MACHDEP,
309 sc->sc_sysctl_me->sysctl_num,
310 CTL_CREATE, CTL_EOL);
311
312 for (i = 0; i < sc->sc_num_fans; i++) {
313 fan = &sc->sc_fans[i];
314
315 for (j = 0; j < strlen(fan->location); j++) {
316 sysctl_fan_name[j] = tolower(fan->location[j]);
317 if (sysctl_fan_name[j] == ' ')
318 sysctl_fan_name[j] = '_';
319 }
320 sysctl_fan_name[j] = '\0';
321
322 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_fan,
323 CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
324 CTLTYPE_NODE, sysctl_fan_name, "fan information",
325 NULL, 0, NULL, 0,
326 CTL_MACHDEP,
327 sc->sc_sysctl_me->sysctl_num,
328 sysctl_fans->sysctl_num,
329 CTL_CREATE, CTL_EOL);
330
331 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_node,
332 CTLFLAG_READONLY | CTLFLAG_OWNDESC,
333 CTLTYPE_INT, "zone", "fan zone",
334 NULL, 0, &fan->zone, 0,
335 CTL_MACHDEP,
336 sc->sc_sysctl_me->sysctl_num,
337 sysctl_fans->sysctl_num,
338 sysctl_fan->sysctl_num,
339 CTL_CREATE, CTL_EOL);
340
341 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_node,
342 CTLFLAG_READONLY | CTLFLAG_OWNDESC,
343 CTLTYPE_INT, "min_rpm", "fan minimum rpm",
344 NULL, 0, &fan->min_rpm, 0,
345 CTL_MACHDEP,
346 sc->sc_sysctl_me->sysctl_num,
347 sysctl_fans->sysctl_num,
348 sysctl_fan->sysctl_num,
349 CTL_CREATE, CTL_EOL);
350
351 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_node,
352 CTLFLAG_READONLY | CTLFLAG_OWNDESC,
353 CTLTYPE_INT, "max_rpm", "fan maximum rpm",
354 NULL, 0, &fan->max_rpm, 0,
355 CTL_MACHDEP,
356 sc->sc_sysctl_me->sysctl_num,
357 sysctl_fans->sysctl_num,
358 sysctl_fan->sysctl_num,
359 CTL_CREATE, CTL_EOL);
360
361 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_node,
362 CTLFLAG_READONLY | CTLFLAG_OWNDESC,
363 CTLTYPE_INT, "default_rpm", "fan default rpm",
364 NULL, 0, &fan->default_rpm, 0,
365 CTL_MACHDEP,
366 sc->sc_sysctl_me->sysctl_num,
367 sysctl_fans->sysctl_num,
368 sysctl_fan->sysctl_num,
369 CTL_CREATE, CTL_EOL);
370
371 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_node,
372 CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
373 CTLTYPE_INT, "rpm", "fan current rpm",
374 smu_sysctl_fan_rpm, 0, (void *) fan, 0,
375 CTL_MACHDEP,
376 sc->sc_sysctl_me->sysctl_num,
377 sysctl_fans->sysctl_num,
378 sysctl_fan->sysctl_num,
379 CTL_CREATE, CTL_EOL);
380 }
381 }
382
383 static void
384 smu_setup_iicbus(struct smu_softc *sc)
385 {
386 struct smu_iicbus *iicbus;
387 struct i2c_controller *i2c;
388 struct smu_iicbus_confargs ca;
389 int node;
390 char name[32];
391
392 mutex_init(&sc->sc_iicbus_lock, MUTEX_DEFAULT, IPL_NONE);
393
394 node = of_getnode_byname(sc->sc_node, "smu-i2c-control");
395 for (node = OF_child(node);
396 (node != 0) && (sc->sc_num_iicbus < SMU_MAX_IICBUS);
397 node = OF_peer(node)) {
398 memset(name, 0, sizeof(name));
399 OF_getprop(node, "name", name, sizeof(name));
400 if (strcmp(name, "i2c-bus") != 0)
401 continue;
402
403 iicbus = &sc->sc_iicbus[sc->sc_num_iicbus];
404 iicbus->sc = sc;
405 i2c = &iicbus->i2c;
406
407 if (OF_getprop(node, "reg", &iicbus->reg, sizeof(iicbus->reg)) <= 0)
408 continue;
409
410 DPRINTF("iicbus: reg %x\n", iicbus->reg);
411
412 i2c->ic_cookie = iicbus;
413 i2c->ic_acquire_bus = smu_iicbus_acquire_bus;
414 i2c->ic_release_bus = smu_iicbus_release_bus;
415 i2c->ic_send_start = NULL;
416 i2c->ic_send_stop = NULL;
417 i2c->ic_initiate_xfer = NULL;
418 i2c->ic_read_byte = NULL;
419 i2c->ic_write_byte = NULL;
420 i2c->ic_exec = smu_iicbus_exec;
421
422 ca.ca_name = name;
423 ca.ca_node = node;
424 ca.ca_tag = i2c;
425 config_found_ia(sc->sc_dev, "smu", &ca, smu_iicbus_print);
426
427 sc->sc_num_iicbus++;
428 }
429 }
430
431 static void
432 smu_setup_sme(struct smu_softc *sc)
433 {
434 struct smu_fan *fan;
435 envsys_data_t *sme_sensor;
436 int i;
437
438 sc->sc_sme = sysmon_envsys_create();
439
440 for (i = 0; i < sc->sc_num_fans; i++) {
441 sme_sensor = &sc->sc_sme_sensors[i];
442 fan = &sc->sc_fans[i];
443
444 sme_sensor->units = ENVSYS_SFANRPM;
445 sme_sensor->state = ENVSYS_SINVALID;
446 snprintf(sme_sensor->desc, sizeof(sme_sensor->desc),
447 "%s", fan->location);
448
449 if (sysmon_envsys_sensor_attach(sc->sc_sme, sme_sensor)) {
450 sysmon_envsys_destroy(sc->sc_sme);
451 return;
452 }
453 }
454
455 sc->sc_sme->sme_name = device_xname(sc->sc_dev);
456 sc->sc_sme->sme_cookie = sc;
457 sc->sc_sme->sme_refresh = smu_sme_refresh;
458
459 if (sysmon_envsys_register(sc->sc_sme)) {
460 aprint_error_dev(sc->sc_dev,
461 "unable to register with sysmon\n");
462 sysmon_envsys_destroy(sc->sc_sme);
463 }
464 }
465
466 static int
467 smu_iicbus_print(void *aux, const char *smu)
468 {
469 struct smu_iicbus_confargs *ca = aux;
470
471 if (smu)
472 aprint_normal("%s at %s", ca->ca_name, smu);
473
474 return UNCONF;
475 }
476
477 static void
478 smu_sme_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
479 {
480 struct smu_softc *sc = sme->sme_cookie;
481 struct smu_fan *fan;
482 int which = edata->sensor;
483 int ret;
484
485 edata->state = ENVSYS_SINVALID;
486
487 if (which < sc->sc_num_fans) {
488 fan = &sc->sc_fans[which];
489
490 ret = smu_fan_get_rpm(fan, &fan->current_rpm);
491 if (ret == 0) {
492 edata->value_cur = fan->current_rpm;
493 edata->state = ENVSYS_SVALID;
494 }
495 }
496 }
497
498 static int
499 smu_do_cmd(struct smu_softc *sc, struct smu_cmd *cmd, int timo)
500 {
501 int gpio, ret;
502 u_char ack;
503
504 mutex_enter(&sc->sc_cmd_lock);
505
506 DPRINTF("%s: cmd %02x len %02x\n", __func__, cmd->cmd, cmd->len);
507 DPRINTF("%s: data %02x %02x %02x %02x %02x %02x %02x %02x\n", __func__,
508 cmd->data[0], cmd->data[1], cmd->data[2], cmd->data[3],
509 cmd->data[4], cmd->data[5], cmd->data[6], cmd->data[7]);
510
511 sc->sc_cmd->cmd = cmd->cmd;
512 sc->sc_cmd->len = cmd->len;
513 memcpy(sc->sc_cmd->data, cmd->data, cmd->len);
514
515 __asm volatile ("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory");
516
517 obio_write_4(sc->sc_dbell_mbox, sc->sc_cmd_paddr);
518 obio_write_1(sc->sc_dbell_gpio, 0x04);
519
520 do {
521 ret = tsleep(sc->sc_cmd, PWAIT, "smu_cmd", (timo * hz) / 1000);
522 if (ret != 0) {
523 mutex_exit(&sc->sc_cmd_lock);
524 return (ret);
525 }
526 gpio = obio_read_1(sc->sc_dbell_gpio);
527 } while ((gpio & 0x07) != 0x07);
528
529 __asm volatile ("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory");
530
531 ack = (~cmd->cmd) & 0xff;
532 if (sc->sc_cmd->cmd != ack) {
533 DPRINTF("%s: invalid ack, got %x expected %x\n",
534 __func__, sc->sc_cmd->cmd, ack);
535 mutex_exit(&sc->sc_cmd_lock);
536 return EIO;
537 }
538
539 cmd->cmd = sc->sc_cmd->cmd;
540 cmd->len = sc->sc_cmd->len;
541 memcpy(cmd->data, sc->sc_cmd->data, sc->sc_cmd->len);
542
543 mutex_exit(&sc->sc_cmd_lock);
544
545 return 0;
546 }
547
548
549 static int
550 smu_dbell_gpio_intr(void *arg)
551 {
552 struct smu_softc *sc = arg;
553
554 DPRINTF("%s\n", __func__);
555
556 wakeup(sc->sc_cmd);
557
558 return 1;
559 }
560
561 void
562 smu_poweroff(void)
563 {
564 struct smu_cmd cmd;
565
566 if (smu0 == NULL)
567 return;
568
569 cmd.cmd = SMU_CMD_POWER;
570 strcpy(cmd.data, "SHUTDOWN");
571 cmd.len = strlen(cmd.data) + 1;
572 smu_do_cmd(smu0, &cmd, 800);
573
574 for (;;);
575 }
576
577 void
578 smu_restart(void)
579 {
580 struct smu_cmd cmd;
581
582 if (smu0 == NULL)
583 return;
584
585 cmd.cmd = SMU_CMD_POWER;
586 strcpy(cmd.data, "RESTART");
587 cmd.len = strlen(cmd.data) + 1;
588 smu_do_cmd(smu0, &cmd, 800);
589
590 for (;;);
591 }
592
593 static int
594 smu_todr_gettime_ymdhms(todr_chip_handle_t tch, struct clock_ymdhms *dt)
595 {
596 struct smu_softc *sc = tch->cookie;
597 struct smu_cmd cmd;
598 int ret;
599
600 cmd.cmd = SMU_CMD_RTC;
601 cmd.len = 1;
602 cmd.data[0] = 0x81;
603
604 ret = smu_do_cmd(sc, &cmd, 800);
605 if (ret != 0)
606 return ret;
607
608 dt->dt_sec = bcdtobin(cmd.data[0]);
609 dt->dt_min = bcdtobin(cmd.data[1]);
610 dt->dt_hour = bcdtobin(cmd.data[2]);
611 dt->dt_wday = bcdtobin(cmd.data[3]);
612 dt->dt_day = bcdtobin(cmd.data[4]);
613 dt->dt_mon = bcdtobin(cmd.data[5]);
614 dt->dt_year = bcdtobin(cmd.data[6]) + 2000;
615
616 return 0;
617 }
618
619 static int
620 smu_todr_settime_ymdhms(todr_chip_handle_t tch, struct clock_ymdhms *dt)
621 {
622 struct smu_softc *sc = tch->cookie;
623 struct smu_cmd cmd;
624
625 cmd.cmd = SMU_CMD_RTC;
626 cmd.len = 8;
627 cmd.data[0] = 0x80;
628 cmd.data[1] = bintobcd(dt->dt_sec);
629 cmd.data[2] = bintobcd(dt->dt_min);
630 cmd.data[3] = bintobcd(dt->dt_hour);
631 cmd.data[4] = bintobcd(dt->dt_wday);
632 cmd.data[5] = bintobcd(dt->dt_day);
633 cmd.data[6] = bintobcd(dt->dt_mon);
634 cmd.data[7] = bintobcd(dt->dt_year - 2000);
635
636 return smu_do_cmd(sc, &cmd, 800);
637 }
638
639 static int
640 smu_fan_update_rpm(struct smu_fan *fan)
641 {
642 struct smu_softc *sc = fan->sc;
643 struct smu_cmd cmd;
644 int ret;
645
646 cmd.cmd = SMU_CMD_FAN;
647 cmd.len = 2;
648 cmd.data[0] = 0x31;
649 cmd.data[1] = fan->reg;
650
651 ret = smu_do_cmd(sc, &cmd, 800);
652 if (ret == 0) {
653 fan->last_update = time_uptime;
654 fan->current_rpm = (cmd.data[0] << 8) | cmd.data[1];
655 } else {
656 cmd.cmd = SMU_CMD_FAN;
657 cmd.len = 1;
658 cmd.data[0] = 0x01;
659
660 ret = smu_do_cmd(sc, &cmd, 800);
661 if (ret == 0) {
662 fan->last_update = time_uptime;
663 fan->current_rpm = (cmd.data[1 + fan->reg * 2] << 8) |
664 cmd.data[2 + fan->reg * 2];
665 }
666 }
667
668 return ret;
669 }
670
671 static int
672 smu_fan_get_rpm(struct smu_fan *fan, int *rpm)
673 {
674 int ret;
675
676 if (time_uptime - fan->last_update > 1) {
677 ret = smu_fan_update_rpm(fan);
678 if (ret != 0)
679 return ret;
680 }
681
682 *rpm = fan->current_rpm;
683
684 return ret;
685 }
686
687 static int
688 smu_fan_set_rpm(struct smu_fan *fan, int rpm)
689 {
690 struct smu_softc *sc = fan->sc;
691 struct smu_cmd cmd;
692 int ret;
693
694 DPRINTF("%s: fan %s rpm %d\n", __func__, fan->location, rpm);
695
696 rpm = max(fan->min_rpm, rpm);
697 rpm = min(fan->max_rpm, rpm);
698
699 cmd.cmd = SMU_CMD_FAN;
700 cmd.len = 4;
701 cmd.data[0] = 0x30;
702 cmd.data[1] = fan->reg;
703 cmd.data[2] = (rpm >> 8) & 0xff;
704 cmd.data[3] = rpm & 0xff;
705
706 ret = smu_do_cmd(sc, &cmd, 800);
707 if (ret != 0) {
708 cmd.cmd = SMU_CMD_FAN;
709 cmd.len = 14;
710 cmd.data[0] = fan->rpm_ctl ? 0x00 : 0x10;
711 cmd.data[1] = 1 << fan->reg;
712 cmd.data[2] = cmd.data[2 + fan->reg * 2] = (rpm >> 8) & 0xff;
713 cmd.data[3] = cmd.data[3 + fan->reg * 2] = rpm & 0xff;
714
715 ret = smu_do_cmd(sc, &cmd, 800);
716 }
717
718 return ret;
719 }
720
721 static int
722 smu_iicbus_acquire_bus(void *cookie, int flags)
723 {
724 struct smu_iicbus *iicbus = cookie;
725 struct smu_softc *sc = iicbus->sc;
726
727 mutex_enter(&sc->sc_iicbus_lock);
728
729 return 0;
730 }
731
732 static void
733 smu_iicbus_release_bus(void *cookie, int flags)
734 {
735 struct smu_iicbus *iicbus = cookie;
736 struct smu_softc *sc = iicbus->sc;
737
738 mutex_exit(&sc->sc_iicbus_lock);
739 }
740
741 static int
742 smu_iicbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *send,
743 size_t send_len, void *recv, size_t recv_len, int flags)
744 {
745 struct smu_iicbus *iicbus = cookie;
746 struct smu_softc *sc = iicbus->sc;
747 struct smu_cmd cmd;
748 int retries, ret;
749
750 DPRINTF("%s: op %x addr %x send_len %d recv_len %d\n",
751 __func__, op, addr, send_len, recv_len);
752
753 cmd.cmd = SMU_CMD_I2C;
754 cmd.len = 9 + recv_len;
755 cmd.data[0] = iicbus->reg;
756 cmd.data[1] = I2C_OP_READ_P(op) ? 0x02 : 0x00;
757 cmd.data[2] = addr;
758 cmd.data[3] = send_len;
759 memcpy(&cmd.data[4], send, send_len);
760 cmd.data[7] = addr;
761 if (I2C_OP_READ_P(op))
762 cmd.data[7] |= 0x01;
763 cmd.data[8] = recv_len;
764 memcpy(&cmd.data[9], recv, recv_len);
765
766 ret = smu_do_cmd(sc, &cmd, 800);
767 if (ret != 0)
768 return (ret);
769
770 for (retries = 0; retries < 10; retries++) {
771 cmd.cmd = SMU_CMD_I2C;
772 cmd.len = 1;
773 cmd.data[0] = 0x00;
774 memset(&cmd.data[1], 0xff, recv_len);
775
776 ret = smu_do_cmd(sc, &cmd, 800);
777
778 DPRINTF("%s: cmd data[0] %x\n", __func__, cmd.data[0]);
779
780 if (ret == 0 && (cmd.data[0] & 0x80) == 0)
781 break;
782
783 DELAY(10000);
784 }
785
786 if (cmd.data[0] & 0x80)
787 return EIO;
788
789 if (I2C_OP_READ_P(op))
790 memcpy(recv, &cmd.data[1], recv_len);
791
792 return 0;
793 }
794
795 static int
796 smu_sysctl_fan_rpm(SYSCTLFN_ARGS)
797 {
798 struct sysctlnode node = *rnode;
799 struct smu_fan *fan = node.sysctl_data;
800 int rpm = 0;
801 int ret;
802
803 node.sysctl_data = &rpm;
804
805 if (newp) {
806 if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
807 rpm = *(int *) node.sysctl_data;
808 return smu_fan_set_rpm(fan, rpm);
809 }
810 return EINVAL;
811 } else {
812 ret = smu_fan_get_rpm(fan, &rpm);
813 if (ret != 0)
814 return (ret);
815
816 return sysctl_lookup(SYSCTLFN_CALL(&node));
817 }
818
819 return 0;
820 }
821
822 SYSCTL_SETUP(smu_sysctl_setup, "SMU sysctl subtree setup")
823 {
824 sysctl_createv(NULL, 0, NULL, NULL,
825 CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
826 NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
827 }
828