smu.c revision 204270
1/*-
2 * Copyright (c) 2009 Nathan Whitehorn
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 AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: head/sys/powerpc/powermac/smu.c 204270 2010-02-24 01:27:36Z nwhitehorn $");
30
31#include <sys/param.h>
32#include <sys/bus.h>
33#include <sys/systm.h>
34#include <sys/module.h>
35#include <sys/callout.h>
36#include <sys/conf.h>
37#include <sys/cpu.h>
38#include <sys/ctype.h>
39#include <sys/kernel.h>
40#include <sys/reboot.h>
41#include <sys/rman.h>
42#include <sys/sysctl.h>
43
44#include <machine/bus.h>
45#include <machine/md_var.h>
46
47#include <dev/led/led.h>
48#include <dev/ofw/openfirm.h>
49#include <dev/ofw/ofw_bus.h>
50#include <powerpc/powermac/macgpiovar.h>
51
52struct smu_cmd {
53	volatile uint8_t cmd;
54	uint8_t		len;
55	uint8_t		data[254];
56};
57
58struct smu_fan {
59	cell_t	reg;
60	cell_t	min_rpm;
61	cell_t	max_rpm;
62	cell_t	unmanaged_rpm;
63	char	location[32];
64
65	int	old_style;
66	int	setpoint;
67};
68
69struct smu_sensor {
70	cell_t	reg;
71	char	location[32];
72	enum {
73		SMU_CURRENT_SENSOR,
74		SMU_VOLTAGE_SENSOR,
75		SMU_POWER_SENSOR,
76		SMU_TEMP_SENSOR
77	} type;
78};
79
80struct smu_softc {
81	device_t	sc_dev;
82	struct mtx	sc_mtx;
83
84	struct resource	*sc_memr;
85	int		sc_memrid;
86
87	bus_dma_tag_t	sc_dmatag;
88	bus_space_tag_t	sc_bt;
89	bus_space_handle_t sc_mailbox;
90
91	struct smu_cmd	*sc_cmd;
92	bus_addr_t	sc_cmd_phys;
93	bus_dmamap_t	sc_cmd_dmamap;
94
95	struct smu_fan	*sc_fans;
96	int		sc_nfans;
97	struct smu_sensor *sc_sensors;
98	int		sc_nsensors;
99
100	struct callout	sc_fanmgt_callout;
101	time_t		sc_lastuserchange;
102
103	/* Calibration data */
104	uint16_t	sc_cpu_diode_scale;
105	int16_t		sc_cpu_diode_offset;
106
107	uint16_t	sc_cpu_volt_scale;
108	int16_t		sc_cpu_volt_offset;
109	uint16_t	sc_cpu_curr_scale;
110	int16_t		sc_cpu_curr_offset;
111
112	uint16_t	sc_slots_pow_scale;
113	int16_t		sc_slots_pow_offset;
114
115	/* Thermal management parameters */
116	int		sc_target_temp;		/* Default 55 C */
117	int		sc_critical_temp;	/* Default 90 C */
118
119	struct cdev 	*sc_leddev;
120};
121
122/* regular bus attachment functions */
123
124static int	smu_probe(device_t);
125static int	smu_attach(device_t);
126
127/* cpufreq notification hooks */
128
129static void	smu_cpufreq_pre_change(device_t, const struct cf_level *level);
130static void	smu_cpufreq_post_change(device_t, const struct cf_level *level);
131
132/* utility functions */
133static int	smu_run_cmd(device_t dev, struct smu_cmd *cmd);
134static int	smu_get_datablock(device_t dev, int8_t id, uint8_t *buf,
135		    size_t len);
136static void	smu_attach_fans(device_t dev, phandle_t fanroot);
137static void	smu_attach_sensors(device_t dev, phandle_t sensroot);
138static void	smu_fanmgt_callout(void *xdev);
139static void	smu_set_sleepled(void *xdev, int onoff);
140static int	smu_server_mode(SYSCTL_HANDLER_ARGS);
141
142/* where to find the doorbell GPIO */
143
144static device_t	smu_doorbell = NULL;
145
146static device_method_t  smu_methods[] = {
147	/* Device interface */
148	DEVMETHOD(device_probe,		smu_probe),
149	DEVMETHOD(device_attach,	smu_attach),
150	{ 0, 0 },
151};
152
153static driver_t smu_driver = {
154	"smu",
155	smu_methods,
156	sizeof(struct smu_softc)
157};
158
159static devclass_t smu_devclass;
160
161DRIVER_MODULE(smu, nexus, smu_driver, smu_devclass, 0, 0);
162MALLOC_DEFINE(M_SMU, "smu", "SMU Sensor Information");
163
164#define SMU_MAILBOX		0x8000860c
165#define SMU_FANMGT_INTERVAL	500 /* ms */
166
167/* Command types */
168#define SMU_ADC			0xd8
169#define SMU_FAN			0x4a
170#define SMU_I2C			0x9a
171#define  SMU_I2C_SIMPLE		0x00
172#define  SMU_I2C_NORMAL		0x01
173#define  SMU_I2C_COMBINED	0x02
174#define SMU_MISC		0xee
175#define  SMU_MISC_GET_DATA	0x02
176#define  SMU_MISC_LED_CTRL	0x04
177#define SMU_POWER		0xaa
178#define SMU_POWER_EVENTS	0x8f
179#define  SMU_PWR_GET_POWERUP	0x00
180#define  SMU_PWR_SET_POWERUP	0x01
181#define  SMU_PWR_CLR_POWERUP	0x02
182
183/* Power event types */
184#define SMU_WAKEUP_KEYPRESS	0x01
185#define SMU_WAKEUP_AC_INSERT	0x02
186#define SMU_WAKEUP_AC_CHANGE	0x04
187#define SMU_WAKEUP_RING		0x10
188
189/* Data blocks */
190#define SMU_CPUTEMP_CAL		0x18
191#define SMU_CPUVOLT_CAL		0x21
192#define SMU_SLOTPW_CAL		0x78
193
194/* Partitions */
195#define SMU_PARTITION		0x3e
196#define SMU_PARTITION_LATEST	0x01
197#define SMU_PARTITION_BASE	0x02
198#define SMU_PARTITION_UPDATE	0x03
199
200static int
201smu_probe(device_t dev)
202{
203	const char *name = ofw_bus_get_name(dev);
204
205	if (strcmp(name, "smu") != 0)
206		return (ENXIO);
207
208	device_set_desc(dev, "Apple System Management Unit");
209	return (0);
210}
211
212static void
213smu_phys_callback(void *xsc, bus_dma_segment_t *segs, int nsegs, int error)
214{
215	struct smu_softc *sc = xsc;
216
217	sc->sc_cmd_phys = segs[0].ds_addr;
218}
219
220static int
221smu_attach(device_t dev)
222{
223	struct smu_softc *sc;
224	phandle_t	node, child;
225	uint8_t		data[12];
226
227	sc = device_get_softc(dev);
228
229	mtx_init(&sc->sc_mtx, "smu", NULL, MTX_DEF);
230
231	/*
232	 * Map the mailbox area. This should be determined from firmware,
233	 * but I have not found a simple way to do that.
234	 */
235	bus_dma_tag_create(NULL, 16, 0, BUS_SPACE_MAXADDR_32BIT,
236	    BUS_SPACE_MAXADDR, NULL, NULL, PAGE_SIZE, 1, PAGE_SIZE, 0, NULL,
237	    NULL, &(sc->sc_dmatag));
238	sc->sc_bt = &bs_le_tag;
239	bus_space_map(sc->sc_bt, SMU_MAILBOX, 4, 0, &sc->sc_mailbox);
240
241	/*
242	 * Allocate the command buffer. This can be anywhere in the low 4 GB
243	 * of memory.
244	 */
245	bus_dmamem_alloc(sc->sc_dmatag, (void **)&sc->sc_cmd, BUS_DMA_WAITOK |
246	    BUS_DMA_ZERO, &sc->sc_cmd_dmamap);
247	bus_dmamap_load(sc->sc_dmatag, sc->sc_cmd_dmamap,
248	    sc->sc_cmd, PAGE_SIZE, smu_phys_callback, sc, 0);
249
250	/*
251	 * Set up handlers to change CPU voltage when CPU frequency is changed.
252	 */
253	EVENTHANDLER_REGISTER(cpufreq_pre_change, smu_cpufreq_pre_change, dev,
254	    EVENTHANDLER_PRI_ANY);
255	EVENTHANDLER_REGISTER(cpufreq_post_change, smu_cpufreq_post_change, dev,
256	    EVENTHANDLER_PRI_ANY);
257
258	/*
259	 * Detect and attach child devices.
260	 */
261	node = ofw_bus_get_node(dev);
262	for (child = OF_child(node); child != 0; child = OF_peer(child)) {
263		char name[32];
264		memset(name, 0, sizeof(name));
265		OF_getprop(child, "name", name, sizeof(name));
266
267		if (strncmp(name, "rpm-fans", 9) == 0 ||
268		    strncmp(name, "fans", 5) == 0)
269			smu_attach_fans(dev, child);
270
271		if (strncmp(name, "sensors", 8) == 0)
272			smu_attach_sensors(dev, child);
273	}
274
275	/*
276	 * Collect calibration constants.
277	 */
278	smu_get_datablock(dev, SMU_CPUTEMP_CAL, data, sizeof(data));
279	sc->sc_cpu_diode_scale = (data[4] << 8) + data[5];
280	sc->sc_cpu_diode_offset = (data[6] << 8) + data[7];
281
282	smu_get_datablock(dev, SMU_CPUVOLT_CAL, data, sizeof(data));
283	sc->sc_cpu_volt_scale = (data[4] << 8) + data[5];
284	sc->sc_cpu_volt_offset = (data[6] << 8) + data[7];
285	sc->sc_cpu_curr_scale = (data[8] << 8) + data[9];
286	sc->sc_cpu_curr_offset = (data[10] << 8) + data[11];
287
288	smu_get_datablock(dev, SMU_SLOTPW_CAL, data, sizeof(data));
289	sc->sc_slots_pow_scale = (data[4] << 8) + data[5];
290	sc->sc_slots_pow_offset = (data[6] << 8) + data[7];
291
292	/*
293	 * Set up simple-minded thermal management.
294	 */
295	sc->sc_target_temp = 55;
296	sc->sc_critical_temp = 90;
297
298	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
299	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
300	    "target_temp", CTLTYPE_INT | CTLFLAG_RW, &sc->sc_target_temp,
301	    sizeof(int), "Target temperature (C)");
302	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
303	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
304	    "critical_temp", CTLTYPE_INT | CTLFLAG_RW,
305	    &sc->sc_critical_temp, sizeof(int), "Critical temperature (C)");
306
307	callout_init(&sc->sc_fanmgt_callout, 1);
308	smu_fanmgt_callout(dev);
309
310	/*
311	 * Set up LED interface
312	 */
313	sc->sc_leddev = led_create(smu_set_sleepled, dev, "sleepled");
314
315	/*
316	 * Reset on power loss behavior
317	 */
318
319	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
320            SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
321	    "server_mode", CTLTYPE_INT | CTLFLAG_RW, dev, 0,
322	    smu_server_mode, "I", "Enable reboot after power failure");
323
324	return (0);
325}
326
327static int
328smu_run_cmd(device_t dev, struct smu_cmd *cmd)
329{
330	struct smu_softc *sc;
331	int doorbell_ack, result, oldpow;
332
333	sc = device_get_softc(dev);
334
335	mtx_lock(&sc->sc_mtx);
336
337	oldpow = powerpc_pow_enabled;
338	powerpc_pow_enabled = 0;
339
340	/* Copy the command to the mailbox */
341	memcpy(sc->sc_cmd, cmd, sizeof(*cmd));
342	bus_dmamap_sync(sc->sc_dmatag, sc->sc_cmd_dmamap, BUS_DMASYNC_PREWRITE);
343	bus_space_write_4(sc->sc_bt, sc->sc_mailbox, 0, sc->sc_cmd_phys);
344
345	/* Flush the cacheline it is in -- SMU bypasses the cache */
346	__asm __volatile("sync; dcbf 0,%0; sync" :: "r"(sc->sc_cmd): "memory");
347
348	/* Ring SMU doorbell */
349	macgpio_write(smu_doorbell, GPIO_DDR_OUTPUT);
350
351	/* Wait for the doorbell GPIO to go high, signaling completion */
352	do {
353		/* XXX: timeout */
354		DELAY(50);
355		doorbell_ack = macgpio_read(smu_doorbell);
356	} while (doorbell_ack != (GPIO_DDR_OUTPUT | GPIO_LEVEL_RO | GPIO_DATA));
357
358	/* Check result. First invalidate the cache again... */
359	__asm __volatile("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory");
360
361	bus_dmamap_sync(sc->sc_dmatag, sc->sc_cmd_dmamap, BUS_DMASYNC_POSTREAD);
362
363	/* SMU acks the command by inverting the command bits */
364	if (sc->sc_cmd->cmd == ((~cmd->cmd) & 0xff))
365		result = 0;
366	else
367		result = EIO;
368
369	powerpc_pow_enabled = oldpow;
370
371	memcpy(cmd->data, sc->sc_cmd->data, sizeof(cmd->data));
372	cmd->len = sc->sc_cmd->len;
373
374	mtx_unlock(&sc->sc_mtx);
375
376	return (result);
377}
378
379static int
380smu_get_datablock(device_t dev, int8_t id, uint8_t *buf, size_t len)
381{
382	struct smu_cmd cmd;
383	uint8_t addr[4];
384
385	cmd.cmd = SMU_PARTITION;
386	cmd.len = 2;
387	cmd.data[0] = SMU_PARTITION_LATEST;
388	cmd.data[1] = id;
389
390	smu_run_cmd(dev, &cmd);
391
392	addr[0] = addr[1] = 0;
393	addr[2] = cmd.data[0];
394	addr[3] = cmd.data[1];
395
396	cmd.cmd = SMU_MISC;
397	cmd.len = 7;
398	cmd.data[0] = SMU_MISC_GET_DATA;
399	cmd.data[1] = sizeof(addr);
400	memcpy(&cmd.data[2], addr, sizeof(addr));
401	cmd.data[6] = len;
402
403	smu_run_cmd(dev, &cmd);
404	memcpy(buf, cmd.data, len);
405	return (0);
406}
407
408static void
409smu_slew_cpu_voltage(device_t dev, int to)
410{
411	struct smu_cmd cmd;
412
413	cmd.cmd = SMU_POWER;
414	cmd.len = 8;
415	cmd.data[0] = 'V';
416	cmd.data[1] = 'S';
417	cmd.data[2] = 'L';
418	cmd.data[3] = 'E';
419	cmd.data[4] = 'W';
420	cmd.data[5] = 0xff;
421	cmd.data[6] = 1;
422	cmd.data[7] = to;
423
424	smu_run_cmd(dev, &cmd);
425}
426
427static void
428smu_cpufreq_pre_change(device_t dev, const struct cf_level *level)
429{
430	/*
431	 * Make sure the CPU voltage is raised before we raise
432	 * the clock.
433	 */
434
435	if (level->rel_set[0].freq == 10000 /* max */)
436		smu_slew_cpu_voltage(dev, 0);
437}
438
439static void
440smu_cpufreq_post_change(device_t dev, const struct cf_level *level)
441{
442	/* We are safe to reduce CPU voltage after a downward transition */
443
444	if (level->rel_set[0].freq < 10000 /* max */)
445		smu_slew_cpu_voltage(dev, 1); /* XXX: 1/4 voltage for 970MP? */
446}
447
448/* Routines for probing the SMU doorbell GPIO */
449static int doorbell_probe(device_t dev);
450static int doorbell_attach(device_t dev);
451
452static device_method_t  doorbell_methods[] = {
453	/* Device interface */
454	DEVMETHOD(device_probe,		doorbell_probe),
455	DEVMETHOD(device_attach,	doorbell_attach),
456	{ 0, 0 },
457};
458
459static driver_t doorbell_driver = {
460	"smudoorbell",
461	doorbell_methods,
462	0
463};
464
465static devclass_t doorbell_devclass;
466
467DRIVER_MODULE(smudoorbell, macgpio, doorbell_driver, doorbell_devclass, 0, 0);
468
469static int
470doorbell_probe(device_t dev)
471{
472	const char *name = ofw_bus_get_name(dev);
473
474	if (strcmp(name, "smu-doorbell") != 0)
475		return (ENXIO);
476
477	device_set_desc(dev, "SMU Doorbell GPIO");
478	device_quiet(dev);
479	return (0);
480}
481
482static int
483doorbell_attach(device_t dev)
484{
485	smu_doorbell = dev;
486	return (0);
487}
488
489/*
490 * Sensor and fan management
491 */
492
493static int
494smu_fan_set_rpm(device_t smu, struct smu_fan *fan, int rpm)
495{
496	struct smu_cmd cmd;
497	int error;
498
499	cmd.cmd = SMU_FAN;
500	error = EIO;
501
502	/* Clamp to allowed range */
503	rpm = max(fan->min_rpm, rpm);
504	rpm = min(fan->max_rpm, rpm);
505
506	/*
507	 * Apple has two fan control mechanisms. We can't distinguish
508	 * them except by seeing if the new one fails. If the new one
509	 * fails, use the old one.
510	 */
511
512	if (!fan->old_style) {
513		cmd.len = 4;
514		cmd.data[0] = 0x30;
515		cmd.data[1] = fan->reg;
516		cmd.data[2] = (rpm >> 8) & 0xff;
517		cmd.data[3] = rpm & 0xff;
518
519		error = smu_run_cmd(smu, &cmd);
520		if (error)
521			fan->old_style = 1;
522	}
523
524	if (fan->old_style) {
525		cmd.len = 14;
526		cmd.data[0] = 0;
527		cmd.data[1] = 1 << fan->reg;
528		cmd.data[2 + 2*fan->reg] = (rpm >> 8) & 0xff;
529		cmd.data[3 + 2*fan->reg] = rpm & 0xff;
530		error = smu_run_cmd(smu, &cmd);
531	}
532
533	if (error == 0)
534		fan->setpoint = rpm;
535
536	return (error);
537}
538
539static int
540smu_fan_read_rpm(device_t smu, struct smu_fan *fan)
541{
542	struct smu_cmd cmd;
543
544	cmd.cmd = SMU_FAN;
545	cmd.len = 1;
546	cmd.data[0] = 1;
547
548	smu_run_cmd(smu, &cmd);
549
550	return ((cmd.data[fan->reg*2+1] << 8) | cmd.data[fan->reg*2+2]);
551}
552
553static int
554smu_fanrpm_sysctl(SYSCTL_HANDLER_ARGS)
555{
556	device_t smu;
557	struct smu_softc *sc;
558	struct smu_fan *fan;
559	int rpm, error;
560
561	smu = arg1;
562	sc = device_get_softc(smu);
563	fan = &sc->sc_fans[arg2];
564
565	rpm = smu_fan_read_rpm(smu, fan);
566	error = sysctl_handle_int(oidp, &rpm, 0, req);
567
568	if (error || !req->newptr)
569		return (error);
570
571	sc->sc_lastuserchange = time_uptime;
572
573	return (smu_fan_set_rpm(smu, fan, rpm));
574}
575
576static void
577smu_attach_fans(device_t dev, phandle_t fanroot)
578{
579	struct smu_fan *fan;
580	struct smu_softc *sc;
581	struct sysctl_oid *oid, *fanroot_oid;
582	struct sysctl_ctx_list *ctx;
583	phandle_t child;
584	char type[32], sysctl_name[32];
585	int i;
586
587	sc = device_get_softc(dev);
588	sc->sc_nfans = 0;
589
590	for (child = OF_child(fanroot); child != 0; child = OF_peer(child))
591		sc->sc_nfans++;
592
593	if (sc->sc_nfans == 0) {
594		device_printf(dev, "WARNING: No fans detected!\n");
595		return;
596	}
597
598	sc->sc_fans = malloc(sc->sc_nfans * sizeof(struct smu_fan), M_SMU,
599	    M_WAITOK | M_ZERO);
600
601	fan = sc->sc_fans;
602	sc->sc_nfans = 0;
603
604	ctx = device_get_sysctl_ctx(dev);
605	fanroot_oid = SYSCTL_ADD_NODE(ctx,
606	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "fans",
607	    CTLFLAG_RD, 0, "SMU Fan Information");
608
609	for (child = OF_child(fanroot); child != 0; child = OF_peer(child)) {
610		OF_getprop(child, "device_type", type, sizeof(type));
611		if (strcmp(type, "fan-rpm-control") != 0)
612			continue;
613
614		fan->old_style = 0;
615		OF_getprop(child, "reg", &fan->reg, sizeof(cell_t));
616		OF_getprop(child, "min-value", &fan->min_rpm, sizeof(cell_t));
617		OF_getprop(child, "max-value", &fan->max_rpm, sizeof(cell_t));
618
619		if (OF_getprop(child, "unmanaged-value", &fan->unmanaged_rpm,
620		    sizeof(cell_t)) != sizeof(cell_t))
621			fan->unmanaged_rpm = fan->max_rpm;
622
623		fan->setpoint = smu_fan_read_rpm(dev, fan);
624
625		OF_getprop(child, "location", fan->location,
626		    sizeof(fan->location));
627
628		/* Add sysctls */
629		for (i = 0; i < strlen(fan->location); i++) {
630			sysctl_name[i] = tolower(fan->location[i]);
631			if (isspace(sysctl_name[i]))
632				sysctl_name[i] = '_';
633		}
634		sysctl_name[i] = 0;
635
636		oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(fanroot_oid),
637		    OID_AUTO, sysctl_name, CTLFLAG_RD, 0, "Fan Information");
638		SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "minrpm",
639		    CTLTYPE_INT | CTLFLAG_RD, &fan->min_rpm, sizeof(cell_t),
640		    "Minimum allowed RPM");
641		SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "maxrpm",
642		    CTLTYPE_INT | CTLFLAG_RD, &fan->max_rpm, sizeof(cell_t),
643		    "Maximum allowed RPM");
644		SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "rpm",
645		    CTLTYPE_INT | CTLFLAG_RW, dev, sc->sc_nfans,
646		    smu_fanrpm_sysctl, "I", "Fan RPM");
647
648		fan++;
649		sc->sc_nfans++;
650	}
651}
652
653static int
654smu_sensor_read(device_t smu, struct smu_sensor *sens)
655{
656	struct smu_cmd cmd;
657	struct smu_softc *sc;
658	int64_t value;
659
660	cmd.cmd = SMU_ADC;
661	cmd.len = 1;
662	cmd.data[0] = sens->reg;
663
664	smu_run_cmd(smu, &cmd);
665
666	sc = device_get_softc(smu);
667	value = (cmd.data[0] << 8) | cmd.data[1];
668
669	switch (sens->type) {
670	case SMU_TEMP_SENSOR:
671		value *= sc->sc_cpu_diode_scale;
672		value >>= 3;
673		value += ((int64_t)sc->sc_cpu_diode_offset) << 9;
674		value <<= 1;
675
676		/* Convert from 16.16 fixed point degC into integer C. */
677		value *= 15625;
678		value /= 1024;
679		value /= 1000000;
680		break;
681	case SMU_VOLTAGE_SENSOR:
682		value *= sc->sc_cpu_volt_scale;
683		value += sc->sc_cpu_volt_offset;
684		value <<= 4;
685
686		/* Convert from 16.16 fixed point V into mV. */
687		value *= 15625;
688		value /= 1024;
689		value /= 1000;
690		break;
691	case SMU_CURRENT_SENSOR:
692		value *= sc->sc_cpu_curr_scale;
693		value += sc->sc_cpu_curr_offset;
694		value <<= 4;
695
696		/* Convert from 16.16 fixed point A into mA. */
697		value *= 15625;
698		value /= 1024;
699		value /= 1000;
700		break;
701	case SMU_POWER_SENSOR:
702		value *= sc->sc_slots_pow_scale;
703		value += sc->sc_slots_pow_offset;
704		value <<= 4;
705
706		/* Convert from 16.16 fixed point W into mW. */
707		value *= 15625;
708		value /= 1024;
709		value /= 1000;
710		break;
711	}
712
713	return (value);
714}
715
716static int
717smu_sensor_sysctl(SYSCTL_HANDLER_ARGS)
718{
719	device_t smu;
720	struct smu_softc *sc;
721	struct smu_sensor *sens;
722	int value, error;
723
724	smu = arg1;
725	sc = device_get_softc(smu);
726	sens = &sc->sc_sensors[arg2];
727
728	value = smu_sensor_read(smu, sens);
729	error = sysctl_handle_int(oidp, &value, 0, req);
730
731	return (error);
732}
733
734static void
735smu_attach_sensors(device_t dev, phandle_t sensroot)
736{
737	struct smu_sensor *sens;
738	struct smu_softc *sc;
739	struct sysctl_oid *sensroot_oid;
740	struct sysctl_ctx_list *ctx;
741	phandle_t child;
742	char type[32];
743	int i;
744
745	sc = device_get_softc(dev);
746	sc->sc_nsensors = 0;
747
748	for (child = OF_child(sensroot); child != 0; child = OF_peer(child))
749		sc->sc_nsensors++;
750
751	if (sc->sc_nsensors == 0) {
752		device_printf(dev, "WARNING: No sensors detected!\n");
753		return;
754	}
755
756	sc->sc_sensors = malloc(sc->sc_nsensors * sizeof(struct smu_sensor),
757	    M_SMU, M_WAITOK | M_ZERO);
758
759	sens = sc->sc_sensors;
760	sc->sc_nsensors = 0;
761
762	ctx = device_get_sysctl_ctx(dev);
763	sensroot_oid = SYSCTL_ADD_NODE(ctx,
764	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensors",
765	    CTLFLAG_RD, 0, "SMU Sensor Information");
766
767	for (child = OF_child(sensroot); child != 0; child = OF_peer(child)) {
768		char sysctl_name[40], sysctl_desc[40];
769		const char *units;
770
771		OF_getprop(child, "device_type", type, sizeof(type));
772
773		if (strcmp(type, "current-sensor") == 0) {
774			sens->type = SMU_CURRENT_SENSOR;
775			units = "mA";
776		} else if (strcmp(type, "temp-sensor") == 0) {
777			sens->type = SMU_TEMP_SENSOR;
778			units = "C";
779		} else if (strcmp(type, "voltage-sensor") == 0) {
780			sens->type = SMU_VOLTAGE_SENSOR;
781			units = "mV";
782		} else if (strcmp(type, "power-sensor") == 0) {
783			sens->type = SMU_POWER_SENSOR;
784			units = "mW";
785		} else {
786			continue;
787		}
788
789		OF_getprop(child, "reg", &sens->reg, sizeof(cell_t));
790		OF_getprop(child, "location", sens->location,
791		    sizeof(sens->location));
792
793		for (i = 0; i < strlen(sens->location); i++) {
794			sysctl_name[i] = tolower(sens->location[i]);
795			if (isspace(sysctl_name[i]))
796				sysctl_name[i] = '_';
797		}
798		sysctl_name[i] = 0;
799
800		sprintf(sysctl_desc,"%s (%s)", sens->location, units);
801
802		SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sensroot_oid), OID_AUTO,
803		    sysctl_name, CTLTYPE_INT | CTLFLAG_RD, dev, sc->sc_nsensors,
804		    smu_sensor_sysctl, "I", sysctl_desc);
805
806		sens++;
807		sc->sc_nsensors++;
808	}
809}
810
811static int
812ms_to_ticks(int ms)
813{
814	if (hz > 1000)
815		return ms*(hz/1000);
816
817	return ms/(1000/hz);
818}
819
820static void
821smu_fanmgt_callout(void *xdev) {
822	device_t smu = xdev;
823	struct smu_softc *sc;
824	int i, maxtemp, temp, factor;
825
826	sc = device_get_softc(smu);
827
828	if (time_uptime - sc->sc_lastuserchange < 3) {
829		/*
830		 * If we have heard from a user process in the last 3 seconds,
831		 * go away.
832		 */
833
834		callout_reset(&sc->sc_fanmgt_callout,
835		    ms_to_ticks(SMU_FANMGT_INTERVAL), smu_fanmgt_callout, smu);
836		return;
837	}
838
839	maxtemp = 0;
840	for (i = 0; i < sc->sc_nsensors; i++) {
841		if (sc->sc_sensors[i].type != SMU_TEMP_SENSOR)
842			continue;
843
844		temp = smu_sensor_read(smu, &sc->sc_sensors[i]);
845		if (temp > maxtemp)
846			maxtemp = temp;
847	}
848
849	if (maxtemp < 10) { /* Bail if no good sensors */
850		for (i = 0; i < sc->sc_nfans; i++)
851			smu_fan_set_rpm(smu, &sc->sc_fans[i],
852			    sc->sc_fans[i].unmanaged_rpm);
853		return;
854	}
855
856	if (maxtemp > sc->sc_critical_temp) {
857		device_printf(smu, "WARNING: Current system temperature (%d C) "
858		    "exceeds critical temperature (%d C)! Shutting down!\n",
859		    maxtemp, sc->sc_critical_temp);
860		shutdown_nice(RB_POWEROFF);
861	}
862
863	if (maxtemp - sc->sc_target_temp > 20)
864		device_printf(smu, "WARNING: Current system temperature (%d C) "
865		    "more than 20 degrees over target temperature (%d C)!\n",
866		    maxtemp, sc->sc_target_temp);
867
868	if (maxtemp > sc->sc_target_temp)
869		factor = 110;
870	else if (sc->sc_target_temp - maxtemp > 4)
871		factor = 90;
872	else if (sc->sc_target_temp - maxtemp > 1)
873		factor = 95;
874	else
875		factor = 100;
876
877	for (i = 0; i < sc->sc_nfans; i++)
878		smu_fan_set_rpm(smu, &sc->sc_fans[i],
879		    (sc->sc_fans[i].setpoint * factor) / 100);
880
881	callout_reset(&sc->sc_fanmgt_callout,
882	    ms_to_ticks(SMU_FANMGT_INTERVAL), smu_fanmgt_callout, smu);
883}
884
885static void
886smu_set_sleepled(void *xdev, int onoff)
887{
888	struct smu_cmd cmd;
889	device_t smu = xdev;
890
891	cmd.cmd = SMU_MISC;
892	cmd.len = 3;
893	cmd.data[0] = SMU_MISC_LED_CTRL;
894	cmd.data[1] = 0;
895	cmd.data[2] = onoff;
896
897	smu_run_cmd(smu, &cmd);
898}
899
900static int
901smu_server_mode(SYSCTL_HANDLER_ARGS)
902{
903	struct smu_cmd cmd;
904	u_int server_mode;
905	device_t smu = arg1;
906	int error;
907
908	cmd.cmd = SMU_POWER_EVENTS;
909	cmd.len = 1;
910	cmd.data[0] = SMU_PWR_GET_POWERUP;
911
912	error = smu_run_cmd(smu, &cmd);
913
914	if (error)
915		return (error);
916
917	server_mode = (cmd.data[1] & SMU_WAKEUP_AC_INSERT) ? 1 : 0;
918
919	error = sysctl_handle_int(oidp, &server_mode, 0, req);
920
921	if (error || !req->newptr)
922		return (error);
923
924	if (server_mode == 1)
925		cmd.data[0] = SMU_PWR_SET_POWERUP;
926	else if (server_mode == 0)
927		cmd.data[0] = SMU_PWR_CLR_POWERUP;
928	else
929		return (EINVAL);
930
931	cmd.len = 3;
932	cmd.data[1] = 0;
933	cmd.data[2] = SMU_WAKEUP_AC_INSERT;
934
935	return (smu_run_cmd(smu, &cmd));
936}
937
938