smu.c revision 1.14.2.1 1 /* $NetBSD: smu.c,v 1.14.2.1 2021/08/09 00:30:08 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2013 Phileas Fogg
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/malloc.h>
33 #include <sys/device.h>
34 #include <sys/proc.h>
35 #include <sys/kmem.h>
36 #include <sys/mutex.h>
37 #include <sys/time.h>
38 #include <sys/reboot.h>
39 #include <sys/sysctl.h>
40 #include <sys/kthread.h>
41
42 #include <machine/autoconf.h>
43
44 #include <dev/ofw/openfirm.h>
45 #include <dev/i2c/i2cvar.h>
46 #include <dev/clock_subr.h>
47 #include <dev/sysmon/sysmonvar.h>
48 #include <dev/sysmon/sysmon_taskq.h>
49
50 #include <macppc/dev/obiovar.h>
51 #include <macppc/dev/smuvar.h>
52
53 #include "opt_smu.h"
54
55 struct smu_softc;
56
57 struct smu_cmd {
58 u_char cmd;
59 u_char len;
60 u_char data[254];
61 };
62
63 struct smu_fan {
64 struct smu_softc* sc;
65
66 char location[32];
67 int reg;
68 int zone;
69 int rpm_ctl;
70 int min_rpm;
71 int max_rpm;
72 int default_rpm;
73 int current_rpm;
74 time_t last_update;
75 };
76
77 struct smu_iicbus {
78 struct smu_softc *sc;
79
80 int reg;
81 struct i2c_controller i2c;
82
83 LIST_ENTRY(smu_iicbus) buslist;
84 };
85
86 #define SMU_MAX_FANS 8
87 #define SMU_MAX_SME_SENSORS (SMU_MAX_FANS + 8)
88
89 struct smu_zone {
90 bool (*filter)(const envsys_data_t *);
91 int nfans;
92 int fans[SMU_MAX_FANS];
93 int threshold, step;
94 int duty;
95 };
96
97
98 #define SMU_ZONE_CPUS 0
99 #define SMU_ZONE_DRIVES 1
100 #define SMU_ZONE_SLOTS 2
101 #define SMU_ZONES 3
102
103 #define C_TO_uK(n) (n * 1000000 + 273150000)
104
105 struct smu_softc {
106 device_t sc_dev;
107 int sc_node;
108 struct sysctlnode *sc_sysctl_me;
109
110 kmutex_t sc_cmd_lock;
111 kmutex_t sc_msg_lock;
112 struct smu_cmd *sc_cmd;
113 paddr_t sc_cmd_paddr;
114 int sc_dbell_mbox;
115 int sc_dbell_gpio;
116
117 int sc_num_fans;
118 struct smu_fan sc_fans[SMU_MAX_FANS];
119
120 LIST_HEAD(, smu_iicbus) sc_iic_busses;
121
122 struct todr_chip_handle sc_todr;
123
124 struct sysmon_envsys *sc_sme;
125 envsys_data_t sc_sme_sensors[SMU_MAX_SME_SENSORS];
126 uint32_t cpu_m;
127 int32_t cpu_b;
128
129 struct smu_zone sc_zones[SMU_ZONES];
130 lwp_t *sc_thread;
131 bool sc_dying;
132 };
133
134 #define SMU_CMD_FAN 0x4a
135 #define SMU_CMD_RTC 0x8e
136 #define SMU_CMD_I2C 0x9a
137 #define SMU_CMD_POWER 0xaa
138 #define SMU_CMD_ADC 0xd8
139 #define SMU_MISC 0xee
140 #define SMU_MISC_GET_DATA 0x02
141 #define SMU_MISC_LED_CTRL 0x04
142
143 #define SMU_CPUTEMP_CAL 0x18
144 #define SMU_CPUVOLT_CAL 0x21
145 #define SMU_SLOTPW_CAL 0x78
146
147 #define SMU_PARTITION 0x3e
148 #define SMU_PARTITION_LATEST 0x01
149 #define SMU_PARTITION_BASE 0x02
150 #define SMU_PARTITION_UPDATE 0x03
151
152 #ifdef SMU_DEBUG
153 #define DPRINTF printf
154 #else
155 #define DPRINTF while (0) printf
156 #endif
157
158 static int smu_match(device_t, struct cfdata *, void *);
159 static void smu_attach(device_t, device_t, void *);
160 static int smu_setup_doorbell(struct smu_softc *);
161 static void smu_setup_fans(struct smu_softc *);
162 static void smu_setup_iicbus(struct smu_softc *);
163 static void smu_setup_sme(struct smu_softc *);
164 static void smu_sme_refresh(struct sysmon_envsys *, envsys_data_t *);
165 static int smu_do_cmd(struct smu_softc *, struct smu_cmd *, int);
166 static int smu_dbell_gpio_intr(void *);
167 static int smu_todr_gettime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *);
168 static int smu_todr_settime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *);
169 static int smu_fan_update_rpm(struct smu_fan *);
170 static int smu_fan_get_rpm(struct smu_fan *, int *);
171 static int smu_fan_set_rpm(struct smu_fan *, int);
172 static int smu_read_adc(struct smu_softc *, int);
173
174 static int smu_iicbus_exec(void *, i2c_op_t, i2c_addr_t, const void *,
175 size_t, void *, size_t, int);
176 static int smu_sysctl_fan_rpm(SYSCTLFN_ARGS);
177
178 static void smu_setup_zones(struct smu_softc *);
179 static void smu_adjust_zone(struct smu_softc *, int);
180 static void smu_adjust(void *);
181 static bool is_cpu_sensor(const envsys_data_t *);
182 static bool is_drive_sensor(const envsys_data_t *);
183 static bool is_slots_sensor(const envsys_data_t *);
184
185 int smu_get_datablock(int, uint8_t *, size_t);
186
187 CFATTACH_DECL_NEW(smu, sizeof(struct smu_softc),
188 smu_match, smu_attach, NULL, NULL);
189
190 static struct smu_softc *smu0 = NULL;
191
192 static int
193 smu_match(device_t parent, struct cfdata *cf, void *aux)
194 {
195 struct confargs *ca = aux;
196
197 if (strcmp(ca->ca_name, "smu") == 0)
198 return 5;
199
200 return 0;
201 }
202
203 static void
204 smu_attach(device_t parent, device_t self, void *aux)
205 {
206 struct confargs *ca = aux;
207 struct smu_softc *sc = device_private(self);
208 uint16_t data[4];
209
210 sc->sc_dev = self;
211 sc->sc_node = ca->ca_node;
212
213 if (smu0 == NULL)
214 smu0 = sc;
215
216 sysctl_createv(NULL, 0, NULL, (void *) &sc->sc_sysctl_me,
217 CTLFLAG_READWRITE,
218 CTLTYPE_NODE, device_xname(sc->sc_dev), NULL,
219 NULL, 0, NULL, 0,
220 CTL_MACHDEP, CTL_CREATE, CTL_EOL);
221
222 if (smu_setup_doorbell(sc) != 0) {
223 aprint_normal(": unable to set up doorbell\n");
224 return;
225 }
226
227 aprint_normal("\n");
228
229 smu_setup_fans(sc);
230 smu_setup_iicbus(sc);
231
232 sc->sc_todr.todr_gettime_ymdhms = smu_todr_gettime_ymdhms;
233 sc->sc_todr.todr_settime_ymdhms = smu_todr_settime_ymdhms;
234 sc->sc_todr.cookie = sc;
235 todr_attach(&sc->sc_todr);
236
237 /* calibration data */
238 memset(data, 0, 8);
239 smu_get_datablock(SMU_CPUTEMP_CAL, (void *)data, 8);
240 DPRINTF("data %04x %04x %04x %04x\n", data[0], data[1], data[2], data[3]);
241 sc->cpu_m = data[2];
242 sc->cpu_b = (int16_t)data[3];
243
244 smu_setup_sme(sc);
245
246 smu_setup_zones(sc);
247 }
248
249 static int
250 smu_setup_doorbell(struct smu_softc *sc)
251 {
252 int node, parent, reg[4], gpio_base, irq;
253
254 mutex_init(&sc->sc_cmd_lock, MUTEX_DEFAULT, IPL_NONE);
255 sc->sc_cmd = malloc(4096, M_DEVBUF, M_WAITOK);
256 sc->sc_cmd_paddr = vtophys((vaddr_t) sc->sc_cmd);
257
258 DPRINTF("%s: cmd vaddr 0x%x paddr 0x%x\n",
259 __func__, (unsigned int) sc->sc_cmd,
260 (unsigned int) sc->sc_cmd_paddr);
261
262 if (OF_getprop(sc->sc_node, "platform-doorbell-buff",
263 &node, sizeof(node)) <= 0)
264 return -1;
265
266 if (OF_getprop(node, "platform-do-doorbell-buff",
267 reg, sizeof(reg)) < sizeof(reg))
268 return -1;
269
270 sc->sc_dbell_mbox = reg[3];
271
272 if (OF_getprop(sc->sc_node, "platform-doorbell-ack",
273 &node, sizeof(node)) <= 0)
274 return -1;
275
276 parent = OF_parent(node);
277 if (parent == 0)
278 return -1;
279
280 if (OF_getprop(parent, "reg", &gpio_base, sizeof(gpio_base)) <= 0)
281 return -1;
282
283 if (OF_getprop(node, "reg", reg, sizeof(reg)) <= 0)
284 return -1;
285
286 if (OF_getprop(node, "interrupts", &irq, sizeof(irq)) <= 0)
287 return -1;
288
289 sc->sc_dbell_gpio = gpio_base + reg[0];
290
291 aprint_normal(" mbox 0x%x gpio 0x%x irq %d",
292 sc->sc_dbell_mbox, sc->sc_dbell_gpio, irq);
293
294 intr_establish_xname(irq, IST_EDGE_FALLING, IPL_TTY,
295 smu_dbell_gpio_intr, sc, device_xname(sc->sc_dev));
296
297 return 0;
298 }
299
300 static void
301 smu_setup_fans(struct smu_softc *sc)
302 {
303 struct smu_fan *fan;
304 struct sysctlnode *sysctl_fans, *sysctl_fan, *sysctl_node;
305 char type[32], sysctl_fan_name[32];
306 int node, i, j;
307 const char *fans[] = { "fans", "rpm-fans", 0 };
308 int n = 0;
309
310 while (fans[n][0] != 0) {
311 node = of_getnode_byname(sc->sc_node, fans[n]);
312 for (node = OF_child(node);
313 (node != 0) && (sc->sc_num_fans < SMU_MAX_FANS);
314 node = OF_peer(node)) {
315 fan = &sc->sc_fans[sc->sc_num_fans];
316 fan->sc = sc;
317
318 memset(fan->location, 0, sizeof(fan->location));
319 OF_getprop(node, "location", fan->location,
320 sizeof(fan->location));
321
322 if (OF_getprop(node, "reg", &fan->reg,
323 sizeof(fan->reg)) <= 0)
324 continue;
325
326 if (OF_getprop(node, "zone", &fan->zone ,
327 sizeof(fan->zone)) <= 0)
328 continue;
329
330 memset(type, 0, sizeof(type));
331 OF_getprop(node, "device_type", type, sizeof(type));
332 if (strcmp(type, "fan-rpm-control") == 0)
333 fan->rpm_ctl = 1;
334 else
335 fan->rpm_ctl = 0;
336
337 if (OF_getprop(node, "min-value", &fan->min_rpm,
338 sizeof(fan->min_rpm)) <= 0)
339 fan->min_rpm = 0;
340
341 if (OF_getprop(node, "max-value", &fan->max_rpm,
342 sizeof(fan->max_rpm)) <= 0)
343 fan->max_rpm = 0xffff;
344
345 if (OF_getprop(node, "unmanage-value", &fan->default_rpm,
346 sizeof(fan->default_rpm)) <= 0)
347 fan->default_rpm = fan->max_rpm;
348
349 DPRINTF("fan: location %s reg %x zone %d rpm_ctl %d "
350 "min_rpm %d max_rpm %d default_rpm %d\n",
351 fan->location, fan->reg, fan->zone, fan->rpm_ctl,
352 fan->min_rpm, fan->max_rpm, fan->default_rpm);
353
354 sc->sc_num_fans++;
355 }
356 n++;
357 }
358
359 for (i = 0; i < sc->sc_num_fans; i++) {
360 fan = &sc->sc_fans[i];
361 smu_fan_set_rpm(fan, fan->default_rpm);
362 smu_fan_get_rpm(fan, &fan->current_rpm);
363 }
364
365 /* Create sysctl nodes for each fan */
366
367 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_fans,
368 CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
369 CTLTYPE_NODE, "fans", NULL,
370 NULL, 0, NULL, 0,
371 CTL_MACHDEP,
372 sc->sc_sysctl_me->sysctl_num,
373 CTL_CREATE, CTL_EOL);
374
375 for (i = 0; i < sc->sc_num_fans; i++) {
376 fan = &sc->sc_fans[i];
377
378 for (j = 0; j < strlen(fan->location); j++) {
379 sysctl_fan_name[j] = tolower(fan->location[j]);
380 if (sysctl_fan_name[j] == ' ')
381 sysctl_fan_name[j] = '_';
382 }
383 sysctl_fan_name[j] = '\0';
384
385 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_fan,
386 CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
387 CTLTYPE_NODE, sysctl_fan_name, "fan information",
388 NULL, 0, NULL, 0,
389 CTL_MACHDEP,
390 sc->sc_sysctl_me->sysctl_num,
391 sysctl_fans->sysctl_num,
392 CTL_CREATE, CTL_EOL);
393
394 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_node,
395 CTLFLAG_READONLY | CTLFLAG_OWNDESC,
396 CTLTYPE_INT, "zone", "fan zone",
397 NULL, 0, &fan->zone, 0,
398 CTL_MACHDEP,
399 sc->sc_sysctl_me->sysctl_num,
400 sysctl_fans->sysctl_num,
401 sysctl_fan->sysctl_num,
402 CTL_CREATE, CTL_EOL);
403
404 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_node,
405 CTLFLAG_READONLY | CTLFLAG_OWNDESC,
406 CTLTYPE_INT, "min_rpm", "fan minimum rpm",
407 NULL, 0, &fan->min_rpm, 0,
408 CTL_MACHDEP,
409 sc->sc_sysctl_me->sysctl_num,
410 sysctl_fans->sysctl_num,
411 sysctl_fan->sysctl_num,
412 CTL_CREATE, CTL_EOL);
413
414 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_node,
415 CTLFLAG_READONLY | CTLFLAG_OWNDESC,
416 CTLTYPE_INT, "max_rpm", "fan maximum rpm",
417 NULL, 0, &fan->max_rpm, 0,
418 CTL_MACHDEP,
419 sc->sc_sysctl_me->sysctl_num,
420 sysctl_fans->sysctl_num,
421 sysctl_fan->sysctl_num,
422 CTL_CREATE, CTL_EOL);
423
424 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_node,
425 CTLFLAG_READONLY | CTLFLAG_OWNDESC,
426 CTLTYPE_INT, "default_rpm", "fan default rpm",
427 NULL, 0, &fan->default_rpm, 0,
428 CTL_MACHDEP,
429 sc->sc_sysctl_me->sysctl_num,
430 sysctl_fans->sysctl_num,
431 sysctl_fan->sysctl_num,
432 CTL_CREATE, CTL_EOL);
433
434 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_node,
435 CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
436 CTLTYPE_INT, "rpm", "fan current rpm",
437 smu_sysctl_fan_rpm, 0, (void *) fan, 0,
438 CTL_MACHDEP,
439 sc->sc_sysctl_me->sysctl_num,
440 sysctl_fans->sysctl_num,
441 sysctl_fan->sysctl_num,
442 CTL_CREATE, CTL_EOL);
443 }
444 }
445
446 static void
447 smu_setup_iicbus(struct smu_softc *sc)
448 {
449 struct smu_iicbus *iicbus;
450 struct i2cbus_attach_args iba;
451 int node;
452 char name[32];
453
454 node = of_getnode_byname(sc->sc_node, "smu-i2c-control");
455 if (node == 0)
456 node = sc->sc_node;
457
458 for (node = OF_child(node); node != 0; node = OF_peer(node)) {
459
460 memset(name, 0, sizeof(name));
461 OF_getprop(node, "name", name, sizeof(name));
462 if (strcmp(name, "i2c-bus") != 0 && strcmp(name, "i2c") != 0)
463 continue;
464
465 iicbus = kmem_zalloc(sizeof(*iicbus), KM_SLEEP);
466 iicbus->sc = sc;
467
468 if (OF_getprop(node, "reg", &iicbus->reg,
469 sizeof(iicbus->reg)) <= 0) {
470 kmem_free(iicbus, sizeof(*iicbus));
471 continue;
472 }
473 LIST_INSERT_HEAD(&sc->sc_iic_busses, iicbus, buslist);
474
475 DPRINTF("iicbus: reg %x\n", iicbus->reg);
476
477 iic_tag_init(&iicbus->i2c);
478 iicbus->i2c.ic_cookie = iicbus;
479 iicbus->i2c.ic_channel = iicbus->reg;
480 iicbus->i2c.ic_exec = smu_iicbus_exec;
481
482 memset(&iba, 0, sizeof(iba));
483 iba.iba_tag = &iicbus->i2c;
484
485 config_found(sc->sc_dev, &iba, iicbus_print_multi,
486 CFARGS(.devhandle = devhandle_from_of(node)));
487 }
488 }
489
490 static void
491 smu_setup_sme(struct smu_softc *sc)
492 {
493 struct smu_fan *fan;
494 envsys_data_t *sme_sensor;
495 int i, sensors, child, reg;
496 char loc[32], type[32];
497
498 sc->sc_sme = sysmon_envsys_create();
499
500 for (i = 0; i < sc->sc_num_fans; i++) {
501 sme_sensor = &sc->sc_sme_sensors[i];
502 fan = &sc->sc_fans[i];
503
504 sme_sensor->units = ENVSYS_SFANRPM;
505 sme_sensor->state = ENVSYS_SINVALID;
506 snprintf(sme_sensor->desc, sizeof(sme_sensor->desc),
507 "%s", fan->location);
508
509 if (sysmon_envsys_sensor_attach(sc->sc_sme, sme_sensor)) {
510 sysmon_envsys_destroy(sc->sc_sme);
511 return;
512 }
513 }
514 sensors = OF_finddevice("/smu/sensors");
515 child = OF_child(sensors);
516 while (child != 0) {
517 sme_sensor = &sc->sc_sme_sensors[i];
518 if (OF_getprop(child, "location", loc, 32) == 0) goto next;
519 if (OF_getprop(child, "device_type", type, 32) == 0) goto next;
520 if (OF_getprop(child, "reg", ®, 4) == 0) goto next;
521 if (strcmp(type, "temp-sensor") == 0) {
522 sme_sensor->units = ENVSYS_STEMP;
523 sme_sensor->state = ENVSYS_SINVALID;
524 strncpy(sme_sensor->desc, loc, sizeof(sme_sensor->desc));
525 sme_sensor->private = reg;
526 sysmon_envsys_sensor_attach(sc->sc_sme, sme_sensor);
527 i++;
528 printf("%s: %s@%x\n", loc, type, reg);
529 }
530 next:
531 child = OF_peer(child);
532 }
533
534 sc->sc_sme->sme_name = device_xname(sc->sc_dev);
535 sc->sc_sme->sme_cookie = sc;
536 sc->sc_sme->sme_refresh = smu_sme_refresh;
537
538 if (sysmon_envsys_register(sc->sc_sme)) {
539 aprint_error_dev(sc->sc_dev,
540 "unable to register with sysmon\n");
541 sysmon_envsys_destroy(sc->sc_sme);
542 }
543 }
544
545 static void
546 smu_sme_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
547 {
548 struct smu_softc *sc = sme->sme_cookie;
549 struct smu_fan *fan;
550 int which = edata->sensor;
551 int ret;
552
553 edata->state = ENVSYS_SINVALID;
554
555 if (which < sc->sc_num_fans) {
556 fan = &sc->sc_fans[which];
557
558 ret = smu_fan_get_rpm(fan, &fan->current_rpm);
559 if (ret == 0) {
560 edata->value_cur = fan->current_rpm;
561 edata->state = ENVSYS_SVALID;
562 }
563 } else if (edata->private > 0) {
564 /* this works only for the CPU diode */
565 int64_t r = smu_read_adc(sc, edata->private);
566 if (r != -1) {
567 r = r * sc->cpu_m;
568 r >>= 3;
569 r += (int64_t)sc->cpu_b << 9;
570 r <<= 1;
571 r *= 15625;
572 r /= 1024;
573 edata->value_cur = r + 273150000;
574 edata->state = ENVSYS_SVALID;
575 }
576 }
577 }
578
579 static int
580 smu_do_cmd(struct smu_softc *sc, struct smu_cmd *cmd, int timo)
581 {
582 int gpio, ret, bail;
583 u_char ack;
584
585 mutex_enter(&sc->sc_cmd_lock);
586
587 DPRINTF("%s: cmd %02x len %02x\n", __func__, cmd->cmd, cmd->len);
588 DPRINTF("%s: data %02x %02x %02x %02x %02x %02x %02x %02x\n", __func__,
589 cmd->data[0], cmd->data[1], cmd->data[2], cmd->data[3],
590 cmd->data[4], cmd->data[5], cmd->data[6], cmd->data[7]);
591
592 sc->sc_cmd->cmd = cmd->cmd;
593 sc->sc_cmd->len = cmd->len;
594 memcpy(sc->sc_cmd->data, cmd->data, cmd->len);
595
596 __asm volatile ("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory");
597
598 obio_write_4(sc->sc_dbell_mbox, sc->sc_cmd_paddr);
599 obio_write_1(sc->sc_dbell_gpio, 0x04);
600
601 bail = 0;
602
603 gpio = obio_read_1(sc->sc_dbell_gpio);
604
605 while (((gpio & 0x07) != 0x07) && (bail < timo)) {
606 ret = tsleep(sc->sc_cmd, PWAIT, "smu_cmd", mstohz(10));
607 if (ret != 0) {
608 bail++;
609 }
610 gpio = obio_read_1(sc->sc_dbell_gpio);
611 }
612
613 if ((gpio & 0x07) != 0x07) {
614 mutex_exit(&sc->sc_cmd_lock);
615 return EWOULDBLOCK;
616 }
617
618 __asm volatile ("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory");
619
620 ack = (~cmd->cmd) & 0xff;
621 if (sc->sc_cmd->cmd != ack) {
622 DPRINTF("%s: invalid ack, got %x expected %x\n",
623 __func__, sc->sc_cmd->cmd, ack);
624 mutex_exit(&sc->sc_cmd_lock);
625 return EIO;
626 }
627
628 cmd->cmd = sc->sc_cmd->cmd;
629 cmd->len = sc->sc_cmd->len;
630 memcpy(cmd->data, sc->sc_cmd->data, sc->sc_cmd->len);
631
632 mutex_exit(&sc->sc_cmd_lock);
633
634 return 0;
635 }
636
637
638 static int
639 smu_dbell_gpio_intr(void *arg)
640 {
641 struct smu_softc *sc = arg;
642
643 DPRINTF("%s\n", __func__);
644
645 wakeup(sc->sc_cmd);
646
647 return 1;
648 }
649
650 void
651 smu_poweroff(void)
652 {
653 struct smu_cmd cmd;
654
655 if (smu0 == NULL)
656 return;
657
658 cmd.cmd = SMU_CMD_POWER;
659 strcpy(cmd.data, "SHUTDOWN");
660 cmd.len = strlen(cmd.data) + 1;
661 smu_do_cmd(smu0, &cmd, 800);
662
663 for (;;);
664 }
665
666 void
667 smu_restart(void)
668 {
669 struct smu_cmd cmd;
670
671 if (smu0 == NULL)
672 return;
673
674 cmd.cmd = SMU_CMD_POWER;
675 strcpy(cmd.data, "RESTART");
676 cmd.len = strlen(cmd.data) + 1;
677 smu_do_cmd(smu0, &cmd, 800);
678
679 for (;;);
680 }
681
682 static int
683 smu_todr_gettime_ymdhms(todr_chip_handle_t tch, struct clock_ymdhms *dt)
684 {
685 struct smu_softc *sc = tch->cookie;
686 struct smu_cmd cmd;
687 int ret;
688
689 cmd.cmd = SMU_CMD_RTC;
690 cmd.len = 1;
691 cmd.data[0] = 0x81;
692
693 ret = smu_do_cmd(sc, &cmd, 800);
694 if (ret != 0)
695 return ret;
696
697 dt->dt_sec = bcdtobin(cmd.data[0]);
698 dt->dt_min = bcdtobin(cmd.data[1]);
699 dt->dt_hour = bcdtobin(cmd.data[2]);
700 dt->dt_wday = bcdtobin(cmd.data[3]);
701 dt->dt_day = bcdtobin(cmd.data[4]);
702 dt->dt_mon = bcdtobin(cmd.data[5]);
703 dt->dt_year = bcdtobin(cmd.data[6]) + 2000;
704
705 return 0;
706 }
707
708 static int
709 smu_todr_settime_ymdhms(todr_chip_handle_t tch, struct clock_ymdhms *dt)
710 {
711 struct smu_softc *sc = tch->cookie;
712 struct smu_cmd cmd;
713
714 cmd.cmd = SMU_CMD_RTC;
715 cmd.len = 8;
716 cmd.data[0] = 0x80;
717 cmd.data[1] = bintobcd(dt->dt_sec);
718 cmd.data[2] = bintobcd(dt->dt_min);
719 cmd.data[3] = bintobcd(dt->dt_hour);
720 cmd.data[4] = bintobcd(dt->dt_wday);
721 cmd.data[5] = bintobcd(dt->dt_day);
722 cmd.data[6] = bintobcd(dt->dt_mon);
723 cmd.data[7] = bintobcd(dt->dt_year - 2000);
724
725 return smu_do_cmd(sc, &cmd, 800);
726 }
727
728 static int
729 smu_fan_update_rpm(struct smu_fan *fan)
730 {
731 struct smu_softc *sc = fan->sc;
732 struct smu_cmd cmd;
733 int ret;
734
735 cmd.cmd = SMU_CMD_FAN;
736 cmd.len = 2;
737 cmd.data[0] = 0x31;
738 cmd.data[1] = fan->reg;
739
740 ret = smu_do_cmd(sc, &cmd, 800);
741 if (ret == 0) {
742 fan->last_update = time_uptime;
743 fan->current_rpm = (cmd.data[0] << 8) | cmd.data[1];
744 } else {
745 cmd.cmd = SMU_CMD_FAN;
746 cmd.len = 1;
747 cmd.data[0] = 0x01;
748
749 ret = smu_do_cmd(sc, &cmd, 800);
750 if (ret == 0) {
751 fan->last_update = time_uptime;
752 fan->current_rpm = (cmd.data[1 + fan->reg * 2] << 8) |
753 cmd.data[2 + fan->reg * 2];
754 }
755 }
756
757 return ret;
758 }
759
760 static int
761 smu_fan_get_rpm(struct smu_fan *fan, int *rpm)
762 {
763 int ret;
764 ret = 0;
765
766 if (time_uptime - fan->last_update > 1) {
767 ret = smu_fan_update_rpm(fan);
768 if (ret != 0)
769 return ret;
770 }
771
772 *rpm = fan->current_rpm;
773
774 return ret;
775 }
776
777 static int
778 smu_fan_set_rpm(struct smu_fan *fan, int rpm)
779 {
780 struct smu_softc *sc = fan->sc;
781 struct smu_cmd cmd;
782 int ret;
783
784 DPRINTF("%s: fan %s rpm %d\n", __func__, fan->location, rpm);
785
786 rpm = uimax(fan->min_rpm, rpm);
787 rpm = uimin(fan->max_rpm, rpm);
788
789 cmd.cmd = SMU_CMD_FAN;
790 cmd.len = 4;
791 cmd.data[0] = 0x30;
792 cmd.data[1] = fan->reg;
793 cmd.data[2] = (rpm >> 8) & 0xff;
794 cmd.data[3] = rpm & 0xff;
795
796 ret = smu_do_cmd(sc, &cmd, 800);
797 if (ret != 0) {
798 cmd.cmd = SMU_CMD_FAN;
799 cmd.len = 14;
800 cmd.data[0] = fan->rpm_ctl ? 0x00 : 0x10;
801 cmd.data[1] = 1 << fan->reg;
802 cmd.data[2] = cmd.data[2 + fan->reg * 2] = (rpm >> 8) & 0xff;
803 cmd.data[3] = cmd.data[3 + fan->reg * 2] = rpm & 0xff;
804
805 ret = smu_do_cmd(sc, &cmd, 800);
806 }
807
808 return ret;
809 }
810
811 static int
812 smu_read_adc(struct smu_softc *sc, int id)
813 {
814 struct smu_cmd cmd;
815 int ret;
816
817 cmd.cmd = SMU_CMD_ADC;
818 cmd.len = 1;
819 cmd.data[0] = id;
820
821 ret = smu_do_cmd(sc, &cmd, 800);
822 if (ret == 0) {
823 return cmd.data[0] << 8 | cmd.data[1];
824 }
825 return -1;
826 }
827
828 static int
829 smu_iicbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *send,
830 size_t send_len, void *recv, size_t recv_len, int flags)
831 {
832 struct smu_iicbus *iicbus = cookie;
833 struct smu_softc *sc = iicbus->sc;
834 struct smu_cmd cmd;
835 int retries, ret;
836
837 DPRINTF("%s: op %x addr %x send_len %d recv_len %d\n",
838 __func__, op, addr, send_len, recv_len);
839
840 cmd.cmd = SMU_CMD_I2C;
841 cmd.len = 9 + recv_len;
842 cmd.data[0] = iicbus->reg;
843 cmd.data[1] = I2C_OP_READ_P(op) ? 0x02 : 0x00;
844 cmd.data[2] = addr << 1;
845 cmd.data[3] = send_len;
846 memcpy(&cmd.data[4], send, send_len);
847 cmd.data[7] = addr << 1;
848 if (I2C_OP_READ_P(op))
849 cmd.data[7] |= 0x01;
850 cmd.data[8] = recv_len;
851 memcpy(&cmd.data[9], recv, recv_len);
852
853 ret = smu_do_cmd(sc, &cmd, 800);
854 if (ret != 0)
855 return (ret);
856
857 for (retries = 0; retries < 10; retries++) {
858 cmd.cmd = SMU_CMD_I2C;
859 cmd.len = 1;
860 cmd.data[0] = 0x00;
861 memset(&cmd.data[1], 0xff, recv_len);
862
863 ret = smu_do_cmd(sc, &cmd, 800);
864
865 DPRINTF("%s: cmd data[0] %x\n", __func__, cmd.data[0]);
866
867 if (ret == 0 && (cmd.data[0] & 0x80) == 0)
868 break;
869
870 DELAY(10000);
871 }
872
873 if (cmd.data[0] & 0x80)
874 return EIO;
875
876 if (I2C_OP_READ_P(op))
877 memcpy(recv, &cmd.data[1], recv_len);
878
879 return 0;
880 }
881
882 static int
883 smu_sysctl_fan_rpm(SYSCTLFN_ARGS)
884 {
885 struct sysctlnode node = *rnode;
886 struct smu_fan *fan = node.sysctl_data;
887 int rpm = 0;
888 int ret;
889
890 node.sysctl_data = &rpm;
891
892 if (newp) {
893 if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
894 rpm = *(int *) node.sysctl_data;
895 return smu_fan_set_rpm(fan, rpm);
896 }
897 return EINVAL;
898 } else {
899 ret = smu_fan_get_rpm(fan, &rpm);
900 if (ret != 0)
901 return (ret);
902
903 return sysctl_lookup(SYSCTLFN_CALL(&node));
904 }
905
906 return 0;
907 }
908
909 SYSCTL_SETUP(smu_sysctl_setup, "SMU sysctl subtree setup")
910 {
911 sysctl_createv(NULL, 0, NULL, NULL,
912 CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
913 NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
914 }
915
916 static void
917 smu_setup_zones(struct smu_softc *sc)
918 {
919 struct smu_zone *z;
920 struct smu_fan *f;
921 int i;
922
923 /* find CPU fans */
924 z = &sc->sc_zones[SMU_ZONE_CPUS];
925 z->nfans = 0;
926 for (i = 0; i < SMU_MAX_FANS; i++) {
927 f = &sc->sc_fans[i];
928 if ((strstr(f->location, "CPU") != NULL) ||
929 (strstr(f->location, "System") != NULL)) {
930 z->fans[z->nfans] = i;
931 z->nfans++;
932 }
933 }
934 aprint_normal_dev(sc->sc_dev,
935 "using %d fans for CPU zone\n", z->nfans);
936 z->threshold = C_TO_uK(45);
937 z->duty = 150;
938 z->step = 3;
939 z->filter = is_cpu_sensor;
940
941 z = &sc->sc_zones[SMU_ZONE_DRIVES];
942 z->nfans = 0;
943 for (i = 0; i < SMU_MAX_FANS; i++) {
944 f = &sc->sc_fans[i];
945 if ((strstr(f->location, "DRIVE") != NULL) ||
946 (strstr(f->location, "Drive") != NULL)) {
947 z->fans[z->nfans] = i;
948 z->nfans++;
949 }
950 }
951 aprint_normal_dev(sc->sc_dev,
952 "using %d fans for drive bay zone\n", z->nfans);
953 z->threshold = C_TO_uK(40);
954 z->duty = 150;
955 z->step = 2;
956 z->filter = is_drive_sensor;
957
958 z = &sc->sc_zones[SMU_ZONE_SLOTS];
959 z->nfans = 0;
960 for (i = 0; i < SMU_MAX_FANS; i++) {
961 f = &sc->sc_fans[i];
962 if ((strstr(f->location, "BACKSIDE") != NULL) ||
963 (strstr(f->location, "SLOTS") != NULL)) {
964 z->fans[z->nfans] = i;
965 z->nfans++;
966 }
967 }
968 aprint_normal_dev(sc->sc_dev,
969 "using %d fans for expansion slots zone\n", z->nfans);
970 z->threshold = C_TO_uK(40);
971 z->duty = 150;
972 z->step = 2;
973 z->filter = is_slots_sensor;
974
975 sc->sc_dying = false;
976 kthread_create(PRI_NONE, 0, curcpu(), smu_adjust, sc, &sc->sc_thread,
977 "fan control");
978 }
979
980 static void
981 smu_adjust_zone(struct smu_softc *sc, int which)
982 {
983 struct smu_zone *z = &sc->sc_zones[which];
984 struct smu_fan *f;
985 long temp, newduty, i, speed, diff;
986
987 DPRINTF("%s %d\n", __func__, which);
988
989 temp = sysmon_envsys_get_max_value(z->filter, true);
990 if (temp == 0) {
991 /* no sensor data - leave fan alone */
992 DPRINTF("nodata\n");
993 return;
994 }
995 DPRINTF("temp %ld ", (temp - 273150000) / 1000000);
996 diff = ((temp - z->threshold) / 1000000) * z->step;
997
998 if (diff < 0) newduty = 0;
999 else if (diff > 100) newduty = 100;
1000 else newduty = diff;
1001
1002 DPRINTF("newduty %ld diff %ld \n", newduty, diff);
1003 if (newduty == z->duty) {
1004 DPRINTF("no change\n");
1005 return;
1006 }
1007 z->duty = newduty;
1008 /* now adjust each fan to the new duty cycle */
1009 for (i = 0; i < z->nfans; i++) {
1010 f = &sc->sc_fans[z->fans[i]];
1011 speed = f->min_rpm + ((f->max_rpm - f->min_rpm) * newduty) / 100;
1012 DPRINTF("fan %d speed %ld ", z->fans[i], speed);
1013 smu_fan_set_rpm(f, speed);
1014 }
1015 DPRINTF("\n");
1016 }
1017
1018 static void
1019 smu_adjust(void *cookie)
1020 {
1021 struct smu_softc *sc = cookie;
1022 int i;
1023
1024 while (!sc->sc_dying) {
1025 for (i = 0; i < SMU_ZONES; i++)
1026 smu_adjust_zone(sc, i);
1027 kpause("fanctrl", true, mstohz(3000), NULL);
1028 }
1029 kthread_exit(0);
1030 }
1031
1032 static bool is_cpu_sensor(const envsys_data_t *edata)
1033 {
1034 if (edata->units != ENVSYS_STEMP)
1035 return false;
1036 if (strstr(edata->desc, "CPU") != NULL)
1037 return TRUE;
1038 return false;
1039 }
1040
1041 static bool is_drive_sensor(const envsys_data_t *edata)
1042 {
1043 if (edata->units != ENVSYS_STEMP)
1044 return false;
1045 if (strstr(edata->desc, "DRIVE") != NULL)
1046 return TRUE;
1047 if (strstr(edata->desc, "drive") != NULL)
1048 return TRUE;
1049 return false;
1050 }
1051
1052 static bool is_slots_sensor(const envsys_data_t *edata)
1053 {
1054 if (edata->units != ENVSYS_STEMP)
1055 return false;
1056 if (strstr(edata->desc, "BACKSIDE") != NULL)
1057 return TRUE;
1058 if (strstr(edata->desc, "INLET") != NULL)
1059 return TRUE;
1060 if (strstr(edata->desc, "DIODE") != NULL)
1061 return TRUE;
1062 if (strstr(edata->desc, "TUNNEL") != NULL)
1063 return TRUE;
1064 return false;
1065 }
1066
1067 int
1068 smu_get_datablock(int id, uint8_t *buf, size_t len)
1069 {
1070 struct smu_cmd cmd;
1071
1072 cmd.cmd = SMU_PARTITION;
1073 cmd.len = 2;
1074 cmd.data[0] = SMU_PARTITION_LATEST;
1075 cmd.data[1] = id;
1076 smu_do_cmd(smu0, &cmd, 100);
1077
1078 cmd.data[4] = cmd.data[0];
1079 cmd.data[5] = cmd.data[1];
1080
1081 cmd.cmd = SMU_MISC;
1082 cmd.len = 7;
1083 cmd.data[0] = SMU_MISC_GET_DATA;
1084 cmd.data[1] = 4;
1085 cmd.data[2] = 0;
1086 cmd.data[3] = 0;
1087 cmd.data[6] = len;
1088 smu_do_cmd(smu0, &cmd, 100);
1089
1090 memcpy(buf, cmd.data, len);
1091 return 0;
1092 }
1093