xref: /netbsd-src/sys/arch/macppc/dev/smu.c (revision 3944ff70a48a86bb622139705a33f8ef461e24f7)
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