smu.c revision 1.13.2.3 1 /* $NetBSD: smu.c,v 1.13.2.3 2021/05/14 01:08:53 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 CFARG_DEVHANDLE, devhandle_from_of(node),
487 CFARG_EOL);
488 }
489 }
490
491 static void
492 smu_setup_sme(struct smu_softc *sc)
493 {
494 struct smu_fan *fan;
495 envsys_data_t *sme_sensor;
496 int i, sensors, child, reg;
497 char loc[32], type[32];
498
499 sc->sc_sme = sysmon_envsys_create();
500
501 for (i = 0; i < sc->sc_num_fans; i++) {
502 sme_sensor = &sc->sc_sme_sensors[i];
503 fan = &sc->sc_fans[i];
504
505 sme_sensor->units = ENVSYS_SFANRPM;
506 sme_sensor->state = ENVSYS_SINVALID;
507 snprintf(sme_sensor->desc, sizeof(sme_sensor->desc),
508 "%s", fan->location);
509
510 if (sysmon_envsys_sensor_attach(sc->sc_sme, sme_sensor)) {
511 sysmon_envsys_destroy(sc->sc_sme);
512 return;
513 }
514 }
515 sensors = OF_finddevice("/smu/sensors");
516 child = OF_child(sensors);
517 while (child != 0) {
518 sme_sensor = &sc->sc_sme_sensors[i];
519 if (OF_getprop(child, "location", loc, 32) == 0) goto next;
520 if (OF_getprop(child, "device_type", type, 32) == 0) goto next;
521 if (OF_getprop(child, "reg", ®, 4) == 0) goto next;
522 if (strcmp(type, "temp-sensor") == 0) {
523 sme_sensor->units = ENVSYS_STEMP;
524 sme_sensor->state = ENVSYS_SINVALID;
525 strncpy(sme_sensor->desc, loc, sizeof(sme_sensor->desc));
526 sme_sensor->private = reg;
527 sysmon_envsys_sensor_attach(sc->sc_sme, sme_sensor);
528 i++;
529 printf("%s: %s@%x\n", loc, type, reg);
530 }
531 next:
532 child = OF_peer(child);
533 }
534
535 sc->sc_sme->sme_name = device_xname(sc->sc_dev);
536 sc->sc_sme->sme_cookie = sc;
537 sc->sc_sme->sme_refresh = smu_sme_refresh;
538
539 if (sysmon_envsys_register(sc->sc_sme)) {
540 aprint_error_dev(sc->sc_dev,
541 "unable to register with sysmon\n");
542 sysmon_envsys_destroy(sc->sc_sme);
543 }
544 }
545
546 static void
547 smu_sme_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
548 {
549 struct smu_softc *sc = sme->sme_cookie;
550 struct smu_fan *fan;
551 int which = edata->sensor;
552 int ret;
553
554 edata->state = ENVSYS_SINVALID;
555
556 if (which < sc->sc_num_fans) {
557 fan = &sc->sc_fans[which];
558
559 ret = smu_fan_get_rpm(fan, &fan->current_rpm);
560 if (ret == 0) {
561 edata->value_cur = fan->current_rpm;
562 edata->state = ENVSYS_SVALID;
563 }
564 } else if (edata->private > 0) {
565 /* this works only for the CPU diode */
566 int64_t r = smu_read_adc(sc, edata->private);
567 if (r != -1) {
568 r = r * sc->cpu_m;
569 r >>= 3;
570 r += (int64_t)sc->cpu_b << 9;
571 r <<= 1;
572 r *= 15625;
573 r /= 1024;
574 edata->value_cur = r + 273150000;
575 edata->state = ENVSYS_SVALID;
576 }
577 }
578 }
579
580 static int
581 smu_do_cmd(struct smu_softc *sc, struct smu_cmd *cmd, int timo)
582 {
583 int gpio, ret, bail;
584 u_char ack;
585
586 mutex_enter(&sc->sc_cmd_lock);
587
588 DPRINTF("%s: cmd %02x len %02x\n", __func__, cmd->cmd, cmd->len);
589 DPRINTF("%s: data %02x %02x %02x %02x %02x %02x %02x %02x\n", __func__,
590 cmd->data[0], cmd->data[1], cmd->data[2], cmd->data[3],
591 cmd->data[4], cmd->data[5], cmd->data[6], cmd->data[7]);
592
593 sc->sc_cmd->cmd = cmd->cmd;
594 sc->sc_cmd->len = cmd->len;
595 memcpy(sc->sc_cmd->data, cmd->data, cmd->len);
596
597 __asm volatile ("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory");
598
599 obio_write_4(sc->sc_dbell_mbox, sc->sc_cmd_paddr);
600 obio_write_1(sc->sc_dbell_gpio, 0x04);
601
602 bail = 0;
603
604 gpio = obio_read_1(sc->sc_dbell_gpio);
605
606 while (((gpio & 0x07) != 0x07) && (bail < timo)) {
607 ret = tsleep(sc->sc_cmd, PWAIT, "smu_cmd", mstohz(10));
608 if (ret != 0) {
609 bail++;
610 }
611 gpio = obio_read_1(sc->sc_dbell_gpio);
612 }
613
614 if ((gpio & 0x07) != 0x07) {
615 mutex_exit(&sc->sc_cmd_lock);
616 return EWOULDBLOCK;
617 }
618
619 __asm volatile ("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory");
620
621 ack = (~cmd->cmd) & 0xff;
622 if (sc->sc_cmd->cmd != ack) {
623 DPRINTF("%s: invalid ack, got %x expected %x\n",
624 __func__, sc->sc_cmd->cmd, ack);
625 mutex_exit(&sc->sc_cmd_lock);
626 return EIO;
627 }
628
629 cmd->cmd = sc->sc_cmd->cmd;
630 cmd->len = sc->sc_cmd->len;
631 memcpy(cmd->data, sc->sc_cmd->data, sc->sc_cmd->len);
632
633 mutex_exit(&sc->sc_cmd_lock);
634
635 return 0;
636 }
637
638
639 static int
640 smu_dbell_gpio_intr(void *arg)
641 {
642 struct smu_softc *sc = arg;
643
644 DPRINTF("%s\n", __func__);
645
646 wakeup(sc->sc_cmd);
647
648 return 1;
649 }
650
651 void
652 smu_poweroff(void)
653 {
654 struct smu_cmd cmd;
655
656 if (smu0 == NULL)
657 return;
658
659 cmd.cmd = SMU_CMD_POWER;
660 strcpy(cmd.data, "SHUTDOWN");
661 cmd.len = strlen(cmd.data) + 1;
662 smu_do_cmd(smu0, &cmd, 800);
663
664 for (;;);
665 }
666
667 void
668 smu_restart(void)
669 {
670 struct smu_cmd cmd;
671
672 if (smu0 == NULL)
673 return;
674
675 cmd.cmd = SMU_CMD_POWER;
676 strcpy(cmd.data, "RESTART");
677 cmd.len = strlen(cmd.data) + 1;
678 smu_do_cmd(smu0, &cmd, 800);
679
680 for (;;);
681 }
682
683 static int
684 smu_todr_gettime_ymdhms(todr_chip_handle_t tch, struct clock_ymdhms *dt)
685 {
686 struct smu_softc *sc = tch->cookie;
687 struct smu_cmd cmd;
688 int ret;
689
690 cmd.cmd = SMU_CMD_RTC;
691 cmd.len = 1;
692 cmd.data[0] = 0x81;
693
694 ret = smu_do_cmd(sc, &cmd, 800);
695 if (ret != 0)
696 return ret;
697
698 dt->dt_sec = bcdtobin(cmd.data[0]);
699 dt->dt_min = bcdtobin(cmd.data[1]);
700 dt->dt_hour = bcdtobin(cmd.data[2]);
701 dt->dt_wday = bcdtobin(cmd.data[3]);
702 dt->dt_day = bcdtobin(cmd.data[4]);
703 dt->dt_mon = bcdtobin(cmd.data[5]);
704 dt->dt_year = bcdtobin(cmd.data[6]) + 2000;
705
706 return 0;
707 }
708
709 static int
710 smu_todr_settime_ymdhms(todr_chip_handle_t tch, struct clock_ymdhms *dt)
711 {
712 struct smu_softc *sc = tch->cookie;
713 struct smu_cmd cmd;
714
715 cmd.cmd = SMU_CMD_RTC;
716 cmd.len = 8;
717 cmd.data[0] = 0x80;
718 cmd.data[1] = bintobcd(dt->dt_sec);
719 cmd.data[2] = bintobcd(dt->dt_min);
720 cmd.data[3] = bintobcd(dt->dt_hour);
721 cmd.data[4] = bintobcd(dt->dt_wday);
722 cmd.data[5] = bintobcd(dt->dt_day);
723 cmd.data[6] = bintobcd(dt->dt_mon);
724 cmd.data[7] = bintobcd(dt->dt_year - 2000);
725
726 return smu_do_cmd(sc, &cmd, 800);
727 }
728
729 static int
730 smu_fan_update_rpm(struct smu_fan *fan)
731 {
732 struct smu_softc *sc = fan->sc;
733 struct smu_cmd cmd;
734 int ret;
735
736 cmd.cmd = SMU_CMD_FAN;
737 cmd.len = 2;
738 cmd.data[0] = 0x31;
739 cmd.data[1] = fan->reg;
740
741 ret = smu_do_cmd(sc, &cmd, 800);
742 if (ret == 0) {
743 fan->last_update = time_uptime;
744 fan->current_rpm = (cmd.data[0] << 8) | cmd.data[1];
745 } else {
746 cmd.cmd = SMU_CMD_FAN;
747 cmd.len = 1;
748 cmd.data[0] = 0x01;
749
750 ret = smu_do_cmd(sc, &cmd, 800);
751 if (ret == 0) {
752 fan->last_update = time_uptime;
753 fan->current_rpm = (cmd.data[1 + fan->reg * 2] << 8) |
754 cmd.data[2 + fan->reg * 2];
755 }
756 }
757
758 return ret;
759 }
760
761 static int
762 smu_fan_get_rpm(struct smu_fan *fan, int *rpm)
763 {
764 int ret;
765 ret = 0;
766
767 if (time_uptime - fan->last_update > 1) {
768 ret = smu_fan_update_rpm(fan);
769 if (ret != 0)
770 return ret;
771 }
772
773 *rpm = fan->current_rpm;
774
775 return ret;
776 }
777
778 static int
779 smu_fan_set_rpm(struct smu_fan *fan, int rpm)
780 {
781 struct smu_softc *sc = fan->sc;
782 struct smu_cmd cmd;
783 int ret;
784
785 DPRINTF("%s: fan %s rpm %d\n", __func__, fan->location, rpm);
786
787 rpm = uimax(fan->min_rpm, rpm);
788 rpm = uimin(fan->max_rpm, rpm);
789
790 cmd.cmd = SMU_CMD_FAN;
791 cmd.len = 4;
792 cmd.data[0] = 0x30;
793 cmd.data[1] = fan->reg;
794 cmd.data[2] = (rpm >> 8) & 0xff;
795 cmd.data[3] = rpm & 0xff;
796
797 ret = smu_do_cmd(sc, &cmd, 800);
798 if (ret != 0) {
799 cmd.cmd = SMU_CMD_FAN;
800 cmd.len = 14;
801 cmd.data[0] = fan->rpm_ctl ? 0x00 : 0x10;
802 cmd.data[1] = 1 << fan->reg;
803 cmd.data[2] = cmd.data[2 + fan->reg * 2] = (rpm >> 8) & 0xff;
804 cmd.data[3] = cmd.data[3 + fan->reg * 2] = rpm & 0xff;
805
806 ret = smu_do_cmd(sc, &cmd, 800);
807 }
808
809 return ret;
810 }
811
812 static int
813 smu_read_adc(struct smu_softc *sc, int id)
814 {
815 struct smu_cmd cmd;
816 int ret;
817
818 cmd.cmd = SMU_CMD_ADC;
819 cmd.len = 1;
820 cmd.data[0] = id;
821
822 ret = smu_do_cmd(sc, &cmd, 800);
823 if (ret == 0) {
824 return cmd.data[0] << 8 | cmd.data[1];
825 }
826 return -1;
827 }
828
829 static int
830 smu_iicbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *send,
831 size_t send_len, void *recv, size_t recv_len, int flags)
832 {
833 struct smu_iicbus *iicbus = cookie;
834 struct smu_softc *sc = iicbus->sc;
835 struct smu_cmd cmd;
836 int retries, ret;
837
838 DPRINTF("%s: op %x addr %x send_len %d recv_len %d\n",
839 __func__, op, addr, send_len, recv_len);
840
841 cmd.cmd = SMU_CMD_I2C;
842 cmd.len = 9 + recv_len;
843 cmd.data[0] = iicbus->reg;
844 cmd.data[1] = I2C_OP_READ_P(op) ? 0x02 : 0x00;
845 cmd.data[2] = addr << 1;
846 cmd.data[3] = send_len;
847 memcpy(&cmd.data[4], send, send_len);
848 cmd.data[7] = addr << 1;
849 if (I2C_OP_READ_P(op))
850 cmd.data[7] |= 0x01;
851 cmd.data[8] = recv_len;
852 memcpy(&cmd.data[9], recv, recv_len);
853
854 ret = smu_do_cmd(sc, &cmd, 800);
855 if (ret != 0)
856 return (ret);
857
858 for (retries = 0; retries < 10; retries++) {
859 cmd.cmd = SMU_CMD_I2C;
860 cmd.len = 1;
861 cmd.data[0] = 0x00;
862 memset(&cmd.data[1], 0xff, recv_len);
863
864 ret = smu_do_cmd(sc, &cmd, 800);
865
866 DPRINTF("%s: cmd data[0] %x\n", __func__, cmd.data[0]);
867
868 if (ret == 0 && (cmd.data[0] & 0x80) == 0)
869 break;
870
871 DELAY(10000);
872 }
873
874 if (cmd.data[0] & 0x80)
875 return EIO;
876
877 if (I2C_OP_READ_P(op))
878 memcpy(recv, &cmd.data[1], recv_len);
879
880 return 0;
881 }
882
883 static int
884 smu_sysctl_fan_rpm(SYSCTLFN_ARGS)
885 {
886 struct sysctlnode node = *rnode;
887 struct smu_fan *fan = node.sysctl_data;
888 int rpm = 0;
889 int ret;
890
891 node.sysctl_data = &rpm;
892
893 if (newp) {
894 if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
895 rpm = *(int *) node.sysctl_data;
896 return smu_fan_set_rpm(fan, rpm);
897 }
898 return EINVAL;
899 } else {
900 ret = smu_fan_get_rpm(fan, &rpm);
901 if (ret != 0)
902 return (ret);
903
904 return sysctl_lookup(SYSCTLFN_CALL(&node));
905 }
906
907 return 0;
908 }
909
910 SYSCTL_SETUP(smu_sysctl_setup, "SMU sysctl subtree setup")
911 {
912 sysctl_createv(NULL, 0, NULL, NULL,
913 CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
914 NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
915 }
916
917 static void
918 smu_setup_zones(struct smu_softc *sc)
919 {
920 struct smu_zone *z;
921 struct smu_fan *f;
922 int i;
923
924 /* find CPU fans */
925 z = &sc->sc_zones[SMU_ZONE_CPUS];
926 z->nfans = 0;
927 for (i = 0; i < SMU_MAX_FANS; i++) {
928 f = &sc->sc_fans[i];
929 if ((strstr(f->location, "CPU") != NULL) ||
930 (strstr(f->location, "System") != NULL)) {
931 z->fans[z->nfans] = i;
932 z->nfans++;
933 }
934 }
935 aprint_normal_dev(sc->sc_dev,
936 "using %d fans for CPU zone\n", z->nfans);
937 z->threshold = C_TO_uK(45);
938 z->duty = 150;
939 z->step = 3;
940 z->filter = is_cpu_sensor;
941
942 z = &sc->sc_zones[SMU_ZONE_DRIVES];
943 z->nfans = 0;
944 for (i = 0; i < SMU_MAX_FANS; i++) {
945 f = &sc->sc_fans[i];
946 if ((strstr(f->location, "DRIVE") != NULL) ||
947 (strstr(f->location, "Drive") != NULL)) {
948 z->fans[z->nfans] = i;
949 z->nfans++;
950 }
951 }
952 aprint_normal_dev(sc->sc_dev,
953 "using %d fans for drive bay zone\n", z->nfans);
954 z->threshold = C_TO_uK(40);
955 z->duty = 150;
956 z->step = 2;
957 z->filter = is_drive_sensor;
958
959 z = &sc->sc_zones[SMU_ZONE_SLOTS];
960 z->nfans = 0;
961 for (i = 0; i < SMU_MAX_FANS; i++) {
962 f = &sc->sc_fans[i];
963 if ((strstr(f->location, "BACKSIDE") != NULL) ||
964 (strstr(f->location, "SLOTS") != NULL)) {
965 z->fans[z->nfans] = i;
966 z->nfans++;
967 }
968 }
969 aprint_normal_dev(sc->sc_dev,
970 "using %d fans for expansion slots zone\n", z->nfans);
971 z->threshold = C_TO_uK(40);
972 z->duty = 150;
973 z->step = 2;
974 z->filter = is_slots_sensor;
975
976 sc->sc_dying = false;
977 kthread_create(PRI_NONE, 0, curcpu(), smu_adjust, sc, &sc->sc_thread,
978 "fan control");
979 }
980
981 static void
982 smu_adjust_zone(struct smu_softc *sc, int which)
983 {
984 struct smu_zone *z = &sc->sc_zones[which];
985 struct smu_fan *f;
986 long temp, newduty, i, speed, diff;
987
988 DPRINTF("%s %d\n", __func__, which);
989
990 temp = sysmon_envsys_get_max_value(z->filter, true);
991 if (temp == 0) {
992 /* no sensor data - leave fan alone */
993 DPRINTF("nodata\n");
994 return;
995 }
996 DPRINTF("temp %ld ", (temp - 273150000) / 1000000);
997 diff = ((temp - z->threshold) / 1000000) * z->step;
998
999 if (diff < 0) newduty = 0;
1000 else if (diff > 100) newduty = 100;
1001 else newduty = diff;
1002
1003 DPRINTF("newduty %ld diff %ld \n", newduty, diff);
1004 if (newduty == z->duty) {
1005 DPRINTF("no change\n");
1006 return;
1007 }
1008 z->duty = newduty;
1009 /* now adjust each fan to the new duty cycle */
1010 for (i = 0; i < z->nfans; i++) {
1011 f = &sc->sc_fans[z->fans[i]];
1012 speed = f->min_rpm + ((f->max_rpm - f->min_rpm) * newduty) / 100;
1013 DPRINTF("fan %d speed %ld ", z->fans[i], speed);
1014 smu_fan_set_rpm(f, speed);
1015 }
1016 DPRINTF("\n");
1017 }
1018
1019 static void
1020 smu_adjust(void *cookie)
1021 {
1022 struct smu_softc *sc = cookie;
1023 int i;
1024
1025 while (!sc->sc_dying) {
1026 for (i = 0; i < SMU_ZONES; i++)
1027 smu_adjust_zone(sc, i);
1028 kpause("fanctrl", true, mstohz(3000), NULL);
1029 }
1030 kthread_exit(0);
1031 }
1032
1033 static bool is_cpu_sensor(const envsys_data_t *edata)
1034 {
1035 if (edata->units != ENVSYS_STEMP)
1036 return false;
1037 if (strstr(edata->desc, "CPU") != NULL)
1038 return TRUE;
1039 return false;
1040 }
1041
1042 static bool is_drive_sensor(const envsys_data_t *edata)
1043 {
1044 if (edata->units != ENVSYS_STEMP)
1045 return false;
1046 if (strstr(edata->desc, "DRIVE") != NULL)
1047 return TRUE;
1048 if (strstr(edata->desc, "drive") != NULL)
1049 return TRUE;
1050 return false;
1051 }
1052
1053 static bool is_slots_sensor(const envsys_data_t *edata)
1054 {
1055 if (edata->units != ENVSYS_STEMP)
1056 return false;
1057 if (strstr(edata->desc, "BACKSIDE") != NULL)
1058 return TRUE;
1059 if (strstr(edata->desc, "INLET") != NULL)
1060 return TRUE;
1061 if (strstr(edata->desc, "DIODE") != NULL)
1062 return TRUE;
1063 if (strstr(edata->desc, "TUNNEL") != NULL)
1064 return TRUE;
1065 return false;
1066 }
1067
1068 int
1069 smu_get_datablock(int id, uint8_t *buf, size_t len)
1070 {
1071 struct smu_cmd cmd;
1072
1073 cmd.cmd = SMU_PARTITION;
1074 cmd.len = 2;
1075 cmd.data[0] = SMU_PARTITION_LATEST;
1076 cmd.data[1] = id;
1077 smu_do_cmd(smu0, &cmd, 100);
1078
1079 cmd.data[4] = cmd.data[0];
1080 cmd.data[5] = cmd.data[1];
1081
1082 cmd.cmd = SMU_MISC;
1083 cmd.len = 7;
1084 cmd.data[0] = SMU_MISC_GET_DATA;
1085 cmd.data[1] = 4;
1086 cmd.data[2] = 0;
1087 cmd.data[3] = 0;
1088 cmd.data[6] = len;
1089 smu_do_cmd(smu0, &cmd, 100);
1090
1091 memcpy(buf, cmd.data, len);
1092 return 0;
1093 }
1094