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 #include <sys/kthread.h>
38 
39 #include <machine/autoconf.h>
40 
41 #include <dev/ofw/openfirm.h>
42 #include <dev/i2c/i2cvar.h>
43 #include <dev/clock_subr.h>
44 #include <dev/sysmon/sysmonvar.h>
45 #include <dev/sysmon/sysmon_taskq.h>
46 
47 #include <macppc/dev/obiovar.h>
48 #include <macppc/dev/smuvar.h>
49 #include <macppc/dev/fancontrolvar.h>
50 
51 #include "opt_smu.h"
52 
53 struct smu_softc;
54 
55 struct smu_cmd {
56           u_char cmd;
57           u_char len;
58           u_char data[254];
59 };
60 
61 struct smu_fan {
62           struct smu_softc* sc;
63 
64           char location[32];
65           int reg;
66           int zone;
67           int rpm_ctl;
68           int min_rpm;
69           int max_rpm;
70           int default_rpm;
71           int wanted_rpm;
72           int current_rpm;
73           int fault;
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 
84 #define SMU_MAX_FANS                    8
85 #define SMU_MAX_IICBUS                  3
86 #define SMU_MAX_SME_SENSORS   (SMU_MAX_FANS + 8)
87 
88 
89 #define SMU_ZONE_CPU                    0
90 #define SMU_ZONE_CASE                   1
91 #define SMU_ZONE_DRIVEBAY     2
92 #define SMU_ZONES             3
93 
94 #define C_TO_uK(n) (n * 1000000 + 273150000)
95 
96 struct smu_softc {
97           device_t sc_dev;
98           int sc_node;
99           struct sysctlnode *sc_sysctl_me;
100 
101           kmutex_t sc_cmd_lock;
102           kmutex_t sc_msg_lock;
103           struct smu_cmd *sc_cmd;
104           paddr_t sc_cmd_paddr;
105           int sc_dbell_mbox;
106           int sc_dbell_gpio;
107 
108           int sc_num_fans;
109           struct smu_fan sc_fans[SMU_MAX_FANS];
110 
111           int sc_num_iicbus;
112           struct smu_iicbus sc_iicbus[SMU_MAX_IICBUS];
113 
114           struct todr_chip_handle sc_todr;
115 
116           struct sysmon_envsys *sc_sme;
117           envsys_data_t sc_sme_sensors[SMU_MAX_SME_SENSORS];
118           uint32_t cpu_m;
119           int32_t  cpu_b;
120 
121           fancontrol_zone_t sc_zones[SMU_ZONES];
122           lwp_t *sc_thread;
123           bool sc_dying;
124 };
125 
126 #define SMU_CMD_FAN 0x4a
127 #define SMU_CMD_RTC 0x8e
128 #define SMU_CMD_I2C 0x9a
129 #define SMU_CMD_POWER         0xaa
130 #define SMU_CMD_ADC 0xd8
131 #define SMU_MISC    0xee
132 #define  SMU_MISC_GET_DATA    0x02
133 #define  SMU_MISC_LED_CTRL    0x04
134 
135 #define SMU_CPUTEMP_CAL 0x18
136 #define SMU_CPUVOLT_CAL       0x21
137 #define SMU_SLOTPW_CAL        0x78
138 
139 #define SMU_PARTITION                   0x3e
140 #define SMU_PARTITION_LATEST  0x01
141 #define SMU_PARTITION_BASE    0x02
142 #define SMU_PARTITION_UPDATE  0x03
143 
144 #ifdef SMU_DEBUG
145 #define DPRINTF printf
146 #else
147 #define DPRINTF while (0) printf
148 #endif
149 
150 static int smu_match(device_t, struct cfdata *, void *);
151 static void smu_attach(device_t, device_t, void *);
152 static int smu_setup_doorbell(struct smu_softc *);
153 static void smu_setup_fans(struct smu_softc *);
154 static void smu_setup_iicbus(struct smu_softc *);
155 static void smu_setup_sme(struct smu_softc *);
156 static int smu_iicbus_print(void *, const char *);
157 static void smu_sme_refresh(struct sysmon_envsys *, envsys_data_t *);
158 static int smu_do_cmd(struct smu_softc *, struct smu_cmd *, int);
159 static int smu_dbell_gpio_intr(void *);
160 static int smu_todr_gettime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *);
161 static int smu_todr_settime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *);
162 static int smu_fan_update_rpm(struct smu_fan *);
163 static int smu_read_adc(struct smu_softc *, int);
164 
165 static int smu_iicbus_exec(void *, i2c_op_t, i2c_addr_t, const void *,
166     size_t, void *, size_t, int);
167 
168 static void smu_setup_zones(struct smu_softc *);
169 static void smu_adjust(void *);
170 
171 static bool is_cpu_sensor(const envsys_data_t *);
172 static bool is_drive_sensor(const envsys_data_t *);
173 static bool is_slots_sensor(const envsys_data_t *);
174 static int smu_fan_get_rpm(void *, int);
175 static int smu_fan_set_rpm(void *, int, int);
176 
177 int smu_get_datablock(int, uint8_t *, size_t);
178 
179 CFATTACH_DECL_NEW(smu, sizeof(struct smu_softc),
180     smu_match, smu_attach, NULL, NULL);
181 
182 static struct smu_softc *smu0 = NULL;
183 
184 static int
smu_match(device_t parent,struct cfdata * cf,void * aux)185 smu_match(device_t parent, struct cfdata *cf, void *aux)
186 {
187           struct confargs *ca = aux;
188 
189           if (strcmp(ca->ca_name, "smu") == 0)
190                     return 5;
191 
192           return 0;
193 }
194 
195 static void
smu_attach(device_t parent,device_t self,void * aux)196 smu_attach(device_t parent, device_t self, void *aux)
197 {
198           struct confargs *ca = aux;
199           struct smu_softc *sc = device_private(self);
200           uint16_t data[4];
201 
202           sc->sc_dev = self;
203           sc->sc_node = ca->ca_node;
204 
205           if (smu0 == NULL)
206                     smu0 = sc;
207 
208           sysctl_createv(NULL, 0, NULL, (void *) &sc->sc_sysctl_me,
209               CTLFLAG_READWRITE,
210               CTLTYPE_NODE, device_xname(sc->sc_dev), NULL,
211               NULL, 0, NULL, 0,
212               CTL_MACHDEP, CTL_CREATE, CTL_EOL);
213 
214           if (smu_setup_doorbell(sc) != 0) {
215                     aprint_normal(": unable to set up doorbell\n");
216                     return;
217           }
218 
219           aprint_normal("\n");
220 
221           smu_setup_fans(sc);
222           smu_setup_iicbus(sc);
223 
224           sc->sc_todr.todr_gettime_ymdhms = smu_todr_gettime_ymdhms;
225           sc->sc_todr.todr_settime_ymdhms = smu_todr_settime_ymdhms;
226           sc->sc_todr.cookie = sc;
227           todr_attach(&sc->sc_todr);
228 
229           /* calibration data */
230           memset(data, 0, 8);
231           smu_get_datablock(SMU_CPUTEMP_CAL, (void *)data, 8);
232           DPRINTF("data %04x %04x %04x %04x\n", data[0], data[1], data[2], data[3]);
233           sc->cpu_m = data[2];
234           sc->cpu_b = (int16_t)data[3];
235 
236           smu_setup_sme(sc);
237 
238           smu_setup_zones(sc);
239 }
240 
241 static int
smu_setup_doorbell(struct smu_softc * sc)242 smu_setup_doorbell(struct smu_softc *sc)
243 {
244           int node, parent, reg[4], gpio_base, irq;
245 
246           mutex_init(&sc->sc_cmd_lock, MUTEX_DEFAULT, IPL_NONE);
247           sc->sc_cmd = malloc(4096, M_DEVBUF, M_WAITOK);
248           sc->sc_cmd_paddr = vtophys((vaddr_t) sc->sc_cmd);
249 
250           DPRINTF("%s: cmd vaddr 0x%x paddr 0x%x\n",
251               __func__, (unsigned int) sc->sc_cmd,
252               (unsigned int) sc->sc_cmd_paddr);
253 
254           if (OF_getprop(sc->sc_node, "platform-doorbell-buff",
255                   &node, sizeof(node)) <= 0)
256                     return -1;
257 
258           if (OF_getprop(node, "platform-do-doorbell-buff",
259                   reg, sizeof(reg)) < sizeof(reg))
260                     return -1;
261 
262           sc->sc_dbell_mbox = reg[3];
263 
264           if (OF_getprop(sc->sc_node, "platform-doorbell-ack",
265                   &node, sizeof(node)) <= 0)
266                     return -1;
267 
268           parent = OF_parent(node);
269           if (parent == 0)
270                     return -1;
271 
272           if (OF_getprop(parent, "reg", &gpio_base, sizeof(gpio_base)) <= 0)
273                     return -1;
274 
275           if (OF_getprop(node, "reg", reg, sizeof(reg)) <= 0)
276                     return -1;
277 
278           if (OF_getprop(node, "interrupts", &irq, sizeof(irq)) <= 0)
279                     return -1;
280 
281           sc->sc_dbell_gpio = gpio_base + reg[0];
282 
283           aprint_normal(" mbox 0x%x gpio 0x%x irq %d",
284               sc->sc_dbell_mbox, sc->sc_dbell_gpio, irq);
285 
286           intr_establish_xname(irq, IST_EDGE_FALLING, IPL_TTY,
287               smu_dbell_gpio_intr, sc, device_xname(sc->sc_dev));
288 
289           return 0;
290 }
291 
292 static void
smu_setup_fans(struct smu_softc * sc)293 smu_setup_fans(struct smu_softc *sc)
294 {
295           struct smu_fan *fan;
296           char type[32];
297           int node, i;
298           const char *fans[] = { "fans", "rpm-fans", 0 };
299           int n = 0;
300 
301           while (fans[n][0] != 0) {
302                     node = of_getnode_byname(sc->sc_node, fans[n]);
303                     for (node = OF_child(node);
304                         (node != 0) && (sc->sc_num_fans < SMU_MAX_FANS);
305                         node = OF_peer(node)) {
306                               fan = &sc->sc_fans[sc->sc_num_fans];
307                               fan->sc = sc;
308 
309                               memset(fan->location, 0, sizeof(fan->location));
310                               OF_getprop(node, "location", fan->location,
311                                   sizeof(fan->location));
312 
313                               if (OF_getprop(node, "reg", &fan->reg,
314                                       sizeof(fan->reg)) <= 0)
315                                         continue;
316 
317                               if (OF_getprop(node, "zone", &fan->zone ,
318                                       sizeof(fan->zone)) <= 0)
319                                         continue;
320 
321                               memset(type, 0, sizeof(type));
322                               OF_getprop(node, "device_type", type, sizeof(type));
323                               if (strcmp(type, "fan-rpm-control") == 0)
324                                         fan->rpm_ctl = 1;
325                               else
326                                         fan->rpm_ctl = 0;
327 
328                               if (OF_getprop(node, "min-value", &fan->min_rpm,
329                                   sizeof(fan->min_rpm)) <= 0)
330                                         fan->min_rpm = 0;
331 
332                               if (OF_getprop(node, "max-value", &fan->max_rpm,
333                                   sizeof(fan->max_rpm)) <= 0)
334                                         fan->max_rpm = 0xffff;
335 
336                               if (OF_getprop(node, "unmanage-value", &fan->default_rpm,
337                                   sizeof(fan->default_rpm)) <= 0)
338                                         fan->default_rpm = fan->max_rpm;
339 
340                               DPRINTF("fan: location %s reg %x zone %d rpm_ctl %d "
341                                   "min_rpm %d max_rpm %d default_rpm %d\n",
342                                   fan->location, fan->reg, fan->zone, fan->rpm_ctl,
343                                   fan->min_rpm, fan->max_rpm, fan->default_rpm);
344 
345                               fan->wanted_rpm = fan->default_rpm;
346                               fan->fault = 0;
347                               sc->sc_num_fans++;
348                     }
349                     n++;
350           }
351 
352           for (i = 0; i < sc->sc_num_fans; i++) {
353                     fan = &sc->sc_fans[i];
354                     smu_fan_set_rpm(sc, i, fan->default_rpm);
355                     smu_fan_update_rpm(fan);
356           }
357 }
358 
359 static void
smu_setup_iicbus(struct smu_softc * sc)360 smu_setup_iicbus(struct smu_softc *sc)
361 {
362           struct smu_iicbus *iicbus;
363           struct i2c_controller *i2c;
364           struct smu_iicbus_confargs ca;
365           int node;
366           char name[32];
367 
368           devhandle_t selfh = device_handle(sc->sc_dev);
369           node = of_getnode_byname(sc->sc_node, "smu-i2c-control");
370           if (node == 0) node = sc->sc_node;
371           for (node = OF_child(node);
372               (node != 0) && (sc->sc_num_iicbus < SMU_MAX_IICBUS);
373               node = OF_peer(node)) {
374                     memset(name, 0, sizeof(name));
375                     OF_getprop(node, "name", name, sizeof(name));
376                     if ((strcmp(name, "i2c-bus") != 0) &&
377                         (strcmp(name, "i2c") != 0))
378                               continue;
379 
380                     iicbus = &sc->sc_iicbus[sc->sc_num_iicbus];
381                     iicbus->sc = sc;
382                     i2c = &iicbus->i2c;
383 
384                     if (OF_getprop(node, "reg", &iicbus->reg, sizeof(iicbus->reg)) <= 0)
385                               continue;
386 
387                     DPRINTF("iicbus: reg %x\n", iicbus->reg);
388 
389                     iic_tag_init(i2c);
390                     i2c->ic_cookie = iicbus;
391                     i2c->ic_exec = smu_iicbus_exec;
392 
393                     ca.ca_name = name;
394                     ca.ca_node = node;
395                     ca.ca_tag = i2c;
396                     config_found(sc->sc_dev, &ca, smu_iicbus_print,
397                         CFARGS(.devhandle = devhandle_from_of(selfh, node)));
398 
399                     sc->sc_num_iicbus++;
400           }
401 }
402 
403 static void
smu_setup_sme(struct smu_softc * sc)404 smu_setup_sme(struct smu_softc *sc)
405 {
406           struct smu_fan *fan;
407           envsys_data_t *sme_sensor;
408           int i, sensors, child, reg;
409           char loc[32], type[32];
410 
411           sc->sc_sme = sysmon_envsys_create();
412 
413           for (i = 0; i < sc->sc_num_fans; i++) {
414                     sme_sensor = &sc->sc_sme_sensors[i];
415                     fan = &sc->sc_fans[i];
416 
417                     sme_sensor->units = ENVSYS_SFANRPM;
418                     sme_sensor->state = ENVSYS_SINVALID;
419                     snprintf(sme_sensor->desc, sizeof(sme_sensor->desc),
420                         "%s", fan->location);
421 
422                     if (sysmon_envsys_sensor_attach(sc->sc_sme, sme_sensor)) {
423                               sysmon_envsys_destroy(sc->sc_sme);
424                               return;
425                     }
426           }
427           sensors = OF_finddevice("/smu/sensors");
428           child = OF_child(sensors);
429           while (child != 0) {
430                     sme_sensor = &sc->sc_sme_sensors[i];
431                     if (OF_getprop(child, "location", loc, 32) == 0) goto next;
432                     if (OF_getprop(child, "device_type", type, 32) == 0) goto next;
433                     if (OF_getprop(child, "reg", &reg, 4) == 0) goto next;
434                     if (strcmp(type, "temp-sensor") == 0) {
435                               sme_sensor->units = ENVSYS_STEMP;
436                               sme_sensor->state = ENVSYS_SINVALID;
437                               strncpy(sme_sensor->desc, loc, sizeof(sme_sensor->desc));
438                               sme_sensor->private = reg;
439                               sysmon_envsys_sensor_attach(sc->sc_sme, sme_sensor);
440                               i++;
441                               printf("%s: %s@%x\n", loc, type, reg);
442                     }
443 next:
444                     child = OF_peer(child);
445           }
446 
447           sc->sc_sme->sme_name = device_xname(sc->sc_dev);
448           sc->sc_sme->sme_cookie = sc;
449           sc->sc_sme->sme_refresh = smu_sme_refresh;
450 
451           if (sysmon_envsys_register(sc->sc_sme)) {
452                     aprint_error_dev(sc->sc_dev,
453                         "unable to register with sysmon\n");
454                     sysmon_envsys_destroy(sc->sc_sme);
455           }
456 }
457 
458 static int
smu_iicbus_print(void * aux,const char * smu)459 smu_iicbus_print(void *aux, const char *smu)
460 {
461           struct smu_iicbus_confargs *ca = aux;
462 
463           if (smu)
464                     aprint_normal("%s at %s", ca->ca_name, smu);
465 
466           return UNCONF;
467 }
468 
469 static void
smu_sme_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)470 smu_sme_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
471 {
472           struct smu_softc *sc = sme->sme_cookie;
473           int which = edata->sensor;
474           int ret;
475 
476           edata->state = ENVSYS_SINVALID;
477 
478           if (which < sc->sc_num_fans) {
479 
480                     ret = smu_fan_get_rpm(sc, which);
481                     if (ret != -1) {
482                               sc->sc_fans[which].current_rpm = ret;
483                               edata->value_cur = ret;
484                               edata->state = ENVSYS_SVALID;
485                     }
486           } else if (edata->private > 0) {
487                     /* this works only for the CPU diode */
488                     int64_t r = smu_read_adc(sc, edata->private);
489                     if (r != -1) {
490                               r = r * sc->cpu_m;
491                               r >>= 3;
492                               r += (int64_t)sc->cpu_b << 9;
493                               r <<= 1;
494                               r *= 15625;
495                               r /= 1024;
496                               edata->value_cur = r + 273150000;
497                               edata->state = ENVSYS_SVALID;
498                     }
499           }
500 }
501 
502 static int
smu_do_cmd(struct smu_softc * sc,struct smu_cmd * cmd,int timo)503 smu_do_cmd(struct smu_softc *sc, struct smu_cmd *cmd, int timo)
504 {
505           int gpio, ret, bail;
506           u_char ack;
507 
508           mutex_enter(&sc->sc_cmd_lock);
509 
510           DPRINTF("%s: cmd %02x len %02x\n", __func__, cmd->cmd, cmd->len);
511           DPRINTF("%s: data %02x %02x %02x %02x %02x %02x %02x %02x\n", __func__,
512               cmd->data[0], cmd->data[1], cmd->data[2], cmd->data[3],
513               cmd->data[4], cmd->data[5], cmd->data[6], cmd->data[7]);
514 
515           sc->sc_cmd->cmd = cmd->cmd;
516           sc->sc_cmd->len = cmd->len;
517           memcpy(sc->sc_cmd->data, cmd->data, cmd->len);
518 
519           __asm volatile ("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory");
520 
521           obio_write_4(sc->sc_dbell_mbox, sc->sc_cmd_paddr);
522           obio_write_1(sc->sc_dbell_gpio, 0x04);
523 
524           bail = 0;
525 
526           gpio = obio_read_1(sc->sc_dbell_gpio);
527 
528           while (((gpio & 0x07) != 0x07) && (bail < timo)) {
529                     ret = tsleep(sc->sc_cmd, PWAIT, "smu_cmd", mstohz(10));
530                     if (ret != 0) {
531                               bail++;
532                     }
533                     gpio = obio_read_1(sc->sc_dbell_gpio);
534           }
535 
536           if ((gpio & 0x07) != 0x07) {
537                     mutex_exit(&sc->sc_cmd_lock);
538                     return EWOULDBLOCK;
539           }
540 
541           __asm volatile ("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory");
542 
543           ack = (~cmd->cmd) & 0xff;
544           if (sc->sc_cmd->cmd != ack) {
545                     DPRINTF("%s: invalid ack, got %x expected %x\n",
546                         __func__, sc->sc_cmd->cmd, ack);
547                     mutex_exit(&sc->sc_cmd_lock);
548                     return EIO;
549           }
550 
551           cmd->cmd = sc->sc_cmd->cmd;
552           cmd->len = sc->sc_cmd->len;
553           memcpy(cmd->data, sc->sc_cmd->data, sc->sc_cmd->len);
554 
555           mutex_exit(&sc->sc_cmd_lock);
556 
557           return 0;
558 }
559 
560 
561 static int
smu_dbell_gpio_intr(void * arg)562 smu_dbell_gpio_intr(void *arg)
563 {
564           struct smu_softc *sc = arg;
565 
566           DPRINTF("%s\n", __func__);
567 
568           wakeup(sc->sc_cmd);
569 
570           return 1;
571 }
572 
573 void
smu_poweroff(void)574 smu_poweroff(void)
575 {
576           struct smu_cmd cmd;
577 
578           if (smu0 == NULL)
579                     return;
580 
581           cmd.cmd = SMU_CMD_POWER;
582           strcpy(cmd.data, "SHUTDOWN");
583           cmd.len = strlen(cmd.data) + 1;
584           smu_do_cmd(smu0, &cmd, 800);
585 
586           for (;;);
587 }
588 
589 void
smu_restart(void)590 smu_restart(void)
591 {
592           struct smu_cmd cmd;
593 
594           if (smu0 == NULL)
595                     return;
596 
597           cmd.cmd = SMU_CMD_POWER;
598           strcpy(cmd.data, "RESTART");
599           cmd.len = strlen(cmd.data) + 1;
600           smu_do_cmd(smu0, &cmd, 800);
601 
602           for (;;);
603 }
604 
605 static int
smu_todr_gettime_ymdhms(todr_chip_handle_t tch,struct clock_ymdhms * dt)606 smu_todr_gettime_ymdhms(todr_chip_handle_t tch, struct clock_ymdhms *dt)
607 {
608           struct smu_softc *sc = tch->cookie;
609           struct smu_cmd cmd;
610           int ret;
611 
612           cmd.cmd = SMU_CMD_RTC;
613           cmd.len = 1;
614           cmd.data[0] = 0x81;
615 
616           ret = smu_do_cmd(sc, &cmd, 800);
617           if (ret != 0)
618                     return ret;
619 
620           dt->dt_sec = bcdtobin(cmd.data[0]);
621           dt->dt_min = bcdtobin(cmd.data[1]);
622           dt->dt_hour = bcdtobin(cmd.data[2]);
623           dt->dt_wday = bcdtobin(cmd.data[3]);
624           dt->dt_day = bcdtobin(cmd.data[4]);
625           dt->dt_mon = bcdtobin(cmd.data[5]);
626           dt->dt_year = bcdtobin(cmd.data[6]) + 2000;
627 
628           return 0;
629 }
630 
631 static int
smu_todr_settime_ymdhms(todr_chip_handle_t tch,struct clock_ymdhms * dt)632 smu_todr_settime_ymdhms(todr_chip_handle_t tch, struct clock_ymdhms *dt)
633 {
634           struct smu_softc *sc = tch->cookie;
635           struct smu_cmd cmd;
636 
637           cmd.cmd = SMU_CMD_RTC;
638           cmd.len = 8;
639           cmd.data[0] = 0x80;
640           cmd.data[1] = bintobcd(dt->dt_sec);
641           cmd.data[2] = bintobcd(dt->dt_min);
642           cmd.data[3] = bintobcd(dt->dt_hour);
643           cmd.data[4] = bintobcd(dt->dt_wday);
644           cmd.data[5] = bintobcd(dt->dt_day);
645           cmd.data[6] = bintobcd(dt->dt_mon);
646           cmd.data[7] = bintobcd(dt->dt_year - 2000);
647 
648           return smu_do_cmd(sc, &cmd, 800);
649 }
650 
651 static int
smu_fan_update_rpm(struct smu_fan * fan)652 smu_fan_update_rpm(struct smu_fan *fan)
653 {
654           struct smu_softc *sc = fan->sc;
655           struct smu_cmd cmd;
656           int ret, diff;
657 
658           cmd.cmd = SMU_CMD_FAN;
659           cmd.len = 2;
660           cmd.data[0] = 0x31;
661           cmd.data[1] = fan->reg;
662 
663           ret = smu_do_cmd(sc, &cmd, 800);
664           if (ret == 0) {
665                     fan->last_update = time_uptime;
666                     fan->current_rpm = (cmd.data[0] << 8) | cmd.data[1];
667           } else {
668                     cmd.cmd = SMU_CMD_FAN;
669                     cmd.len = 1;
670                     cmd.data[0] = 0x01;
671 
672                     ret = smu_do_cmd(sc, &cmd, 800);
673                     if (ret == 0) {
674                               fan->last_update = time_uptime;
675                               fan->current_rpm = (cmd.data[1 + fan->reg * 2] << 8) |
676                                   cmd.data[2 + fan->reg * 2];
677                     }
678           }
679           diff = abs(fan->current_rpm - fan->wanted_rpm);
680           if (diff > fan->max_rpm >> 3) {
681                     fan->fault++;
682           } else fan->fault = 0;
683           return ret;
684 }
685 
686 static int
smu_fan_get_rpm(void * cookie,int which)687 smu_fan_get_rpm(void *cookie, int which)
688 {
689           struct smu_softc *sc = cookie;
690           struct smu_fan *fan = &sc->sc_fans[which];
691           int ret;
692           ret = 0;
693 
694           if (time_uptime - fan->last_update > 1) {
695                     ret = smu_fan_update_rpm(fan);
696                     if (ret != 0)
697                               return -1;
698           }
699 
700           return fan->current_rpm;
701 }
702 
703 static int
smu_fan_set_rpm(void * cookie,int which,int rpm)704 smu_fan_set_rpm(void *cookie, int which, int rpm)
705 {
706           struct smu_softc *sc = cookie;
707           struct smu_fan *fan = &sc->sc_fans[which];
708           struct smu_cmd cmd;
709           int ret;
710 
711           DPRINTF("%s: fan %s rpm %d\n", __func__, fan->location, rpm);
712 
713           rpm = uimax(fan->min_rpm, rpm);
714           rpm = uimin(fan->max_rpm, rpm);
715 
716           fan->wanted_rpm = rpm;
717 
718           cmd.cmd = SMU_CMD_FAN;
719           cmd.len = 4;
720           cmd.data[0] = 0x30;
721           cmd.data[1] = fan->reg;
722           cmd.data[2] = (rpm >> 8) & 0xff;
723           cmd.data[3] = rpm & 0xff;
724 
725           ret = smu_do_cmd(sc, &cmd, 800);
726           if (ret != 0) {
727                     cmd.cmd = SMU_CMD_FAN;
728                     cmd.len = 14;
729                     cmd.data[0] = fan->rpm_ctl ? 0x00 : 0x10;
730                     cmd.data[1] = 1 << fan->reg;
731                     cmd.data[2] = cmd.data[2 + fan->reg * 2] = (rpm >> 8) & 0xff;
732                     cmd.data[3] = cmd.data[3 + fan->reg * 2] = rpm & 0xff;
733 
734                     ret = smu_do_cmd(sc, &cmd, 800);
735           }
736 
737           return ret;
738 }
739 
740 static int
smu_read_adc(struct smu_softc * sc,int id)741 smu_read_adc(struct smu_softc *sc, int id)
742 {
743           struct smu_cmd cmd;
744           int ret;
745 
746           cmd.cmd = SMU_CMD_ADC;
747           cmd.len = 1;
748           cmd.data[0] = id;
749 
750           ret = smu_do_cmd(sc, &cmd, 800);
751           if (ret == 0) {
752                     return cmd.data[0] << 8 | cmd.data[1];
753           }
754           return -1;
755 }
756 
757 static int
smu_iicbus_exec(void * cookie,i2c_op_t op,i2c_addr_t addr,const void * send,size_t send_len,void * recv,size_t recv_len,int flags)758 smu_iicbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *send,
759     size_t send_len, void *recv, size_t recv_len, int flags)
760 {
761           struct smu_iicbus *iicbus = cookie;
762           struct smu_softc *sc = iicbus->sc;
763           struct smu_cmd cmd;
764           int retries, ret;
765 
766           DPRINTF("%s: op %x addr %x send_len %d recv_len %d\n",
767               __func__, op, addr, send_len, recv_len);
768 
769           cmd.cmd = SMU_CMD_I2C;
770           cmd.len = 9 + recv_len;
771           cmd.data[0] = iicbus->reg;
772           cmd.data[1] = I2C_OP_READ_P(op) ? 0x02 : 0x00;
773           cmd.data[2] = addr << 1;
774           cmd.data[3] = send_len;
775           memcpy(&cmd.data[4], send, send_len);
776           cmd.data[7] = addr << 1;
777           if (I2C_OP_READ_P(op))
778                     cmd.data[7] |= 0x01;
779           cmd.data[8] = recv_len;
780           memcpy(&cmd.data[9], recv, recv_len);
781 
782           ret = smu_do_cmd(sc, &cmd, 800);
783           if (ret != 0)
784                     return (ret);
785 
786           for (retries = 0; retries < 10; retries++) {
787                     cmd.cmd = SMU_CMD_I2C;
788                     cmd.len = 1;
789                     cmd.data[0] = 0x00;
790                     memset(&cmd.data[1], 0xff, recv_len);
791 
792                     ret = smu_do_cmd(sc, &cmd, 800);
793 
794                     DPRINTF("%s: cmd data[0] %x\n", __func__, cmd.data[0]);
795 
796                     if (ret == 0 && (cmd.data[0] & 0x80) == 0)
797                               break;
798 
799                     DELAY(10000);
800           }
801 
802           if (cmd.data[0] & 0x80)
803                     return EIO;
804 
805           if (I2C_OP_READ_P(op))
806                     memcpy(recv, &cmd.data[1], recv_len);
807 
808           return 0;
809 }
810 
811 SYSCTL_SETUP(smu_sysctl_setup, "SMU sysctl subtree setup")
812 {
813           sysctl_createv(NULL, 0, NULL, NULL,
814               CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
815               NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
816 }
817 
818 static void
smu_setup_zones(struct smu_softc * sc)819 smu_setup_zones(struct smu_softc *sc)
820 {
821           struct smu_fan *f;
822           fancontrol_zone_t *z;
823           int i;
824 
825           /* init zones */
826           sc->sc_zones[SMU_ZONE_CPU].name = "CPUs";
827           sc->sc_zones[SMU_ZONE_CPU].filter = is_cpu_sensor;
828           sc->sc_zones[SMU_ZONE_CPU].cookie = sc;
829           sc->sc_zones[SMU_ZONE_CPU].get_rpm = smu_fan_get_rpm;
830           sc->sc_zones[SMU_ZONE_CPU].set_rpm = smu_fan_set_rpm;
831           sc->sc_zones[SMU_ZONE_CPU].Tmin = 45;
832           sc->sc_zones[SMU_ZONE_CPU].Tmax = 80;
833           sc->sc_zones[SMU_ZONE_CPU].nfans = 0;
834           sc->sc_zones[SMU_ZONE_CASE].name = "Slots";
835           sc->sc_zones[SMU_ZONE_CASE].filter = is_slots_sensor;
836           sc->sc_zones[SMU_ZONE_CASE].cookie = sc;
837           sc->sc_zones[SMU_ZONE_CASE].Tmin = 50;
838           sc->sc_zones[SMU_ZONE_CASE].Tmax = 75;
839           sc->sc_zones[SMU_ZONE_CASE].nfans = 0;
840           sc->sc_zones[SMU_ZONE_CASE].get_rpm = smu_fan_get_rpm;
841           sc->sc_zones[SMU_ZONE_CASE].set_rpm = smu_fan_set_rpm;
842           sc->sc_zones[SMU_ZONE_DRIVEBAY].name = "Drivebays";
843           sc->sc_zones[SMU_ZONE_DRIVEBAY].filter = is_drive_sensor;
844           sc->sc_zones[SMU_ZONE_DRIVEBAY].cookie = sc;
845           sc->sc_zones[SMU_ZONE_DRIVEBAY].get_rpm = smu_fan_get_rpm;
846           sc->sc_zones[SMU_ZONE_DRIVEBAY].set_rpm = smu_fan_set_rpm;
847           sc->sc_zones[SMU_ZONE_DRIVEBAY].Tmin = 30;
848           sc->sc_zones[SMU_ZONE_DRIVEBAY].Tmax = 50;
849           sc->sc_zones[SMU_ZONE_DRIVEBAY].nfans = 0;
850 
851           /* find CPU fans */
852           z = &sc->sc_zones[SMU_ZONE_CPU];
853           for (i = 0; i < SMU_MAX_FANS; i++) {
854                     f = &sc->sc_fans[i];
855                     if ((strstr(f->location, "CPU") != NULL) ||
856                         (strstr(f->location, "System") != NULL)) {
857                               z->fans[z->nfans].num = i;
858                               z->fans[z->nfans].min_rpm = f->min_rpm;
859                               z->fans[z->nfans].max_rpm = f->max_rpm;
860                               z->fans[z->nfans].name = f->location;
861                               z->nfans++;
862                     }
863           }
864           aprint_normal_dev(sc->sc_dev,
865               "using %d fans for CPU zone\n", z->nfans);
866 
867           z = &sc->sc_zones[SMU_ZONE_DRIVEBAY];
868           for (i = 0; i < SMU_MAX_FANS; i++) {
869                     f = &sc->sc_fans[i];
870                     if ((strstr(f->location, "DRIVE") != NULL) ||
871                         (strstr(f->location, "Drive") != NULL)) {
872                               z->fans[z->nfans].num = i;
873                               z->fans[z->nfans].min_rpm = f->min_rpm;
874                               z->fans[z->nfans].max_rpm = f->max_rpm;
875                               z->fans[z->nfans].name = f->location;
876                               z->nfans++;
877                     }
878           }
879           aprint_normal_dev(sc->sc_dev,
880               "using %d fans for drive bay zone\n", z->nfans);
881 
882           z = &sc->sc_zones[SMU_ZONE_CASE];
883           for (i = 0; i < SMU_MAX_FANS; i++) {
884                     f = &sc->sc_fans[i];
885                     if ((strstr(f->location, "BACKSIDE") != NULL) ||
886                         (strstr(f->location, "SLOTS") != NULL)) {
887                               z->fans[z->nfans].num = i;
888                               z->fans[z->nfans].min_rpm = f->min_rpm;
889                               z->fans[z->nfans].max_rpm = f->max_rpm;
890                               z->fans[z->nfans].name = f->location;
891                               z->nfans++;
892                     }
893           }
894           aprint_normal_dev(sc->sc_dev,
895               "using %d fans for expansion slots zone\n", z->nfans);
896 
897           /* setup sysctls for our zones etc. */
898           for (i = 0; i < SMU_ZONES; i++) {
899                     fancontrol_init_zone(&sc->sc_zones[i], sc->sc_sysctl_me);
900           }
901 
902           sc->sc_dying = false;
903           kthread_create(PRI_NONE, 0, curcpu(), smu_adjust, sc, &sc->sc_thread,
904               "fan control");
905 }
906 
907 static void
smu_adjust(void * cookie)908 smu_adjust(void *cookie)
909 {
910           struct smu_softc *sc = cookie;
911           int i;
912 
913           while (!sc->sc_dying) {
914                     for (i = 0; i < SMU_ZONES; i++)
915                               if (sc->sc_zones[i].nfans > 0)
916                                         fancontrol_adjust_zone(&sc->sc_zones[i]);
917                     kpause("fanctrl", true, mstohz(2000), NULL);
918           }
919           kthread_exit(0);
920 }
921 
is_cpu_sensor(const envsys_data_t * edata)922 static bool is_cpu_sensor(const envsys_data_t *edata)
923 {
924           if (edata->units != ENVSYS_STEMP)
925                     return false;
926           if (strstr(edata->desc, "CPU") != NULL)
927                     return TRUE;
928           return false;
929 }
930 
is_drive_sensor(const envsys_data_t * edata)931 static bool is_drive_sensor(const envsys_data_t *edata)
932 {
933           if (edata->units != ENVSYS_STEMP)
934                     return false;
935           if (strstr(edata->desc, "DRIVE") != NULL)
936                     return TRUE;
937           if (strstr(edata->desc, "drive") != NULL)
938                     return TRUE;
939           return false;
940 }
941 
is_slots_sensor(const envsys_data_t * edata)942 static bool is_slots_sensor(const envsys_data_t *edata)
943 {
944           if (edata->units != ENVSYS_STEMP)
945                     return false;
946           if (strstr(edata->desc, "BACKSIDE") != NULL)
947                     return TRUE;
948           if (strstr(edata->desc, "INLET") != NULL)
949                     return TRUE;
950           if (strstr(edata->desc, "DIODE") != NULL)
951                     return TRUE;
952           if (strstr(edata->desc, "TUNNEL") != NULL)
953                     return TRUE;
954           return false;
955 }
956 
957 int
smu_get_datablock(int id,uint8_t * buf,size_t len)958 smu_get_datablock(int id, uint8_t *buf, size_t len)
959 {
960           struct smu_cmd cmd;
961 
962           cmd.cmd = SMU_PARTITION;
963           cmd.len = 2;
964           cmd.data[0] = SMU_PARTITION_LATEST;
965           cmd.data[1] = id;
966           smu_do_cmd(smu0, &cmd, 100);
967 
968           cmd.data[4] = cmd.data[0];
969           cmd.data[5] = cmd.data[1];
970 
971           cmd.cmd = SMU_MISC;
972           cmd.len = 7;
973           cmd.data[0] = SMU_MISC_GET_DATA;
974           cmd.data[1] = 4;
975           cmd.data[2] = 0;
976           cmd.data[3] = 0;
977           cmd.data[6] = len;
978           smu_do_cmd(smu0, &cmd, 100);
979 
980           memcpy(buf, cmd.data, len);
981           return 0;
982 }
983