1194679Snwhitehorn/*-
2194679Snwhitehorn * Copyright (c) 2009 Nathan Whitehorn
3194679Snwhitehorn * All rights reserved.
4194679Snwhitehorn *
5194679Snwhitehorn * Redistribution and use in source and binary forms, with or without
6194679Snwhitehorn * modification, are permitted provided that the following conditions
7194679Snwhitehorn * are met:
8194679Snwhitehorn * 1. Redistributions of source code must retain the above copyright
9194679Snwhitehorn *    notice, this list of conditions and the following disclaimer.
10194679Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright
11194679Snwhitehorn *    notice, this list of conditions and the following disclaimer in the
12194679Snwhitehorn *    documentation and/or other materials provided with the distribution.
13194679Snwhitehorn *
14194679Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15194679Snwhitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16194679Snwhitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17194679Snwhitehorn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18194679Snwhitehorn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19194679Snwhitehorn * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20194679Snwhitehorn * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21194679Snwhitehorn * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22194679Snwhitehorn * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23194679Snwhitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24194679Snwhitehorn * SUCH DAMAGE.
25194679Snwhitehorn *
26194679Snwhitehorn */
27194679Snwhitehorn
28194679Snwhitehorn#include <sys/cdefs.h>
29194679Snwhitehorn__FBSDID("$FreeBSD$");
30194679Snwhitehorn
31194679Snwhitehorn#include <sys/param.h>
32194679Snwhitehorn#include <sys/bus.h>
33194679Snwhitehorn#include <sys/systm.h>
34194679Snwhitehorn#include <sys/module.h>
35194679Snwhitehorn#include <sys/conf.h>
36194679Snwhitehorn#include <sys/cpu.h>
37205506Snwhitehorn#include <sys/clock.h>
38204082Snwhitehorn#include <sys/ctype.h>
39194679Snwhitehorn#include <sys/kernel.h>
40204692Snwhitehorn#include <sys/kthread.h>
41204180Snwhitehorn#include <sys/reboot.h>
42194679Snwhitehorn#include <sys/rman.h>
43194679Snwhitehorn#include <sys/sysctl.h>
44204692Snwhitehorn#include <sys/unistd.h>
45194679Snwhitehorn
46194679Snwhitehorn#include <machine/bus.h>
47204692Snwhitehorn#include <machine/intr_machdep.h>
48194679Snwhitehorn#include <machine/md_var.h>
49194679Snwhitehorn
50208841Snwhitehorn#include <dev/iicbus/iicbus.h>
51208841Snwhitehorn#include <dev/iicbus/iiconf.h>
52204218Snwhitehorn#include <dev/led/led.h>
53194679Snwhitehorn#include <dev/ofw/openfirm.h>
54194679Snwhitehorn#include <dev/ofw/ofw_bus.h>
55208841Snwhitehorn#include <dev/ofw/ofw_bus_subr.h>
56194679Snwhitehorn#include <powerpc/powermac/macgpiovar.h>
57222429Snwhitehorn#include <powerpc/powermac/powermac_thermal.h>
58194679Snwhitehorn
59205506Snwhitehorn#include "clock_if.h"
60208841Snwhitehorn#include "iicbus_if.h"
61205506Snwhitehorn
62194679Snwhitehornstruct smu_cmd {
63204082Snwhitehorn	volatile uint8_t cmd;
64194679Snwhitehorn	uint8_t		len;
65194679Snwhitehorn	uint8_t		data[254];
66204692Snwhitehorn
67204692Snwhitehorn	STAILQ_ENTRY(smu_cmd) cmd_q;
68194679Snwhitehorn};
69194679Snwhitehorn
70204692SnwhitehornSTAILQ_HEAD(smu_cmdq, smu_cmd);
71204692Snwhitehorn
72204082Snwhitehornstruct smu_fan {
73222429Snwhitehorn	struct pmac_fan fan;
74222429Snwhitehorn	device_t dev;
75204082Snwhitehorn	cell_t	reg;
76204180Snwhitehorn
77232482Sandreast	enum {
78232482Sandreast		SMU_FAN_RPM,
79232482Sandreast		SMU_FAN_PWM
80232482Sandreast	} type;
81250290Snwhitehorn	int	setpoint;
82204179Snwhitehorn	int	old_style;
83232482Sandreast	int     rpm;
84204082Snwhitehorn};
85204082Snwhitehorn
86232482Sandreast/* We can read the PWM and the RPM from a PWM controlled fan.
87232482Sandreast * Offer both values via sysctl.
88232482Sandreast */
89232482Sandreastenum {
90232482Sandreast	SMU_PWM_SYSCTL_PWM   = 1 << 8,
91232482Sandreast	SMU_PWM_SYSCTL_RPM   = 2 << 8
92232482Sandreast};
93232482Sandreast
94204082Snwhitehornstruct smu_sensor {
95222429Snwhitehorn	struct pmac_therm therm;
96222429Snwhitehorn	device_t dev;
97222429Snwhitehorn
98204082Snwhitehorn	cell_t	reg;
99204082Snwhitehorn	enum {
100204082Snwhitehorn		SMU_CURRENT_SENSOR,
101204082Snwhitehorn		SMU_VOLTAGE_SENSOR,
102204082Snwhitehorn		SMU_POWER_SENSOR,
103204082Snwhitehorn		SMU_TEMP_SENSOR
104204082Snwhitehorn	} type;
105204082Snwhitehorn};
106204082Snwhitehorn
107194679Snwhitehornstruct smu_softc {
108194679Snwhitehorn	device_t	sc_dev;
109194679Snwhitehorn	struct mtx	sc_mtx;
110194679Snwhitehorn
111194679Snwhitehorn	struct resource	*sc_memr;
112194679Snwhitehorn	int		sc_memrid;
113215100Snwhitehorn	int		sc_u3;
114194679Snwhitehorn
115194679Snwhitehorn	bus_dma_tag_t	sc_dmatag;
116194679Snwhitehorn	bus_space_tag_t	sc_bt;
117194679Snwhitehorn	bus_space_handle_t sc_mailbox;
118194679Snwhitehorn
119204692Snwhitehorn	struct smu_cmd	*sc_cmd, *sc_cur_cmd;
120194679Snwhitehorn	bus_addr_t	sc_cmd_phys;
121194679Snwhitehorn	bus_dmamap_t	sc_cmd_dmamap;
122204692Snwhitehorn	struct smu_cmdq	sc_cmdq;
123204082Snwhitehorn
124204082Snwhitehorn	struct smu_fan	*sc_fans;
125204082Snwhitehorn	int		sc_nfans;
126250290Snwhitehorn	int		old_style_fans;
127204082Snwhitehorn	struct smu_sensor *sc_sensors;
128204082Snwhitehorn	int		sc_nsensors;
129204082Snwhitehorn
130204692Snwhitehorn	int		sc_doorbellirqid;
131204692Snwhitehorn	struct resource	*sc_doorbellirq;
132204692Snwhitehorn	void		*sc_doorbellirqcookie;
133204692Snwhitehorn
134204692Snwhitehorn	struct proc	*sc_fanmgt_proc;
135204180Snwhitehorn	time_t		sc_lastuserchange;
136204180Snwhitehorn
137204082Snwhitehorn	/* Calibration data */
138204082Snwhitehorn	uint16_t	sc_cpu_diode_scale;
139204082Snwhitehorn	int16_t		sc_cpu_diode_offset;
140204082Snwhitehorn
141204082Snwhitehorn	uint16_t	sc_cpu_volt_scale;
142204082Snwhitehorn	int16_t		sc_cpu_volt_offset;
143204082Snwhitehorn	uint16_t	sc_cpu_curr_scale;
144204082Snwhitehorn	int16_t		sc_cpu_curr_offset;
145204082Snwhitehorn
146204082Snwhitehorn	uint16_t	sc_slots_pow_scale;
147204082Snwhitehorn	int16_t		sc_slots_pow_offset;
148204180Snwhitehorn
149204218Snwhitehorn	struct cdev 	*sc_leddev;
150194679Snwhitehorn};
151194679Snwhitehorn
152194679Snwhitehorn/* regular bus attachment functions */
153194679Snwhitehorn
154194679Snwhitehornstatic int	smu_probe(device_t);
155194679Snwhitehornstatic int	smu_attach(device_t);
156208841Snwhitehornstatic const struct ofw_bus_devinfo *
157208841Snwhitehorn    smu_get_devinfo(device_t bus, device_t dev);
158194679Snwhitehorn
159194679Snwhitehorn/* cpufreq notification hooks */
160194679Snwhitehorn
161194679Snwhitehornstatic void	smu_cpufreq_pre_change(device_t, const struct cf_level *level);
162194679Snwhitehornstatic void	smu_cpufreq_post_change(device_t, const struct cf_level *level);
163194679Snwhitehorn
164205506Snwhitehorn/* clock interface */
165205506Snwhitehornstatic int	smu_gettime(device_t dev, struct timespec *ts);
166205506Snwhitehornstatic int	smu_settime(device_t dev, struct timespec *ts);
167205506Snwhitehorn
168204082Snwhitehorn/* utility functions */
169204692Snwhitehornstatic int	smu_run_cmd(device_t dev, struct smu_cmd *cmd, int wait);
170204082Snwhitehornstatic int	smu_get_datablock(device_t dev, int8_t id, uint8_t *buf,
171204082Snwhitehorn		    size_t len);
172208841Snwhitehornstatic void	smu_attach_i2c(device_t dev, phandle_t i2croot);
173204082Snwhitehornstatic void	smu_attach_fans(device_t dev, phandle_t fanroot);
174204082Snwhitehornstatic void	smu_attach_sensors(device_t dev, phandle_t sensroot);
175204218Snwhitehornstatic void	smu_set_sleepled(void *xdev, int onoff);
176204270Snwhitehornstatic int	smu_server_mode(SYSCTL_HANDLER_ARGS);
177204692Snwhitehornstatic void	smu_doorbell_intr(void *xdev);
178212054Snwhitehornstatic void	smu_shutdown(void *xdev, int howto);
179204082Snwhitehorn
180194679Snwhitehorn/* where to find the doorbell GPIO */
181194679Snwhitehorn
182194679Snwhitehornstatic device_t	smu_doorbell = NULL;
183194679Snwhitehorn
184194679Snwhitehornstatic device_method_t  smu_methods[] = {
185194679Snwhitehorn	/* Device interface */
186194679Snwhitehorn	DEVMETHOD(device_probe,		smu_probe),
187194679Snwhitehorn	DEVMETHOD(device_attach,	smu_attach),
188205506Snwhitehorn
189205506Snwhitehorn	/* Clock interface */
190205506Snwhitehorn	DEVMETHOD(clock_gettime,	smu_gettime),
191205506Snwhitehorn	DEVMETHOD(clock_settime,	smu_settime),
192208841Snwhitehorn
193208841Snwhitehorn	/* ofw_bus interface */
194208841Snwhitehorn	DEVMETHOD(bus_child_pnpinfo_str,ofw_bus_gen_child_pnpinfo_str),
195208841Snwhitehorn	DEVMETHOD(ofw_bus_get_devinfo,	smu_get_devinfo),
196208841Snwhitehorn	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
197208841Snwhitehorn	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
198208841Snwhitehorn	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
199208841Snwhitehorn	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
200208841Snwhitehorn	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
201208841Snwhitehorn
202194679Snwhitehorn	{ 0, 0 },
203194679Snwhitehorn};
204194679Snwhitehorn
205194679Snwhitehornstatic driver_t smu_driver = {
206194679Snwhitehorn	"smu",
207194679Snwhitehorn	smu_methods,
208194679Snwhitehorn	sizeof(struct smu_softc)
209194679Snwhitehorn};
210194679Snwhitehorn
211194679Snwhitehornstatic devclass_t smu_devclass;
212194679Snwhitehorn
213261513SnwhitehornDRIVER_MODULE(smu, ofwbus, smu_driver, smu_devclass, 0, 0);
214227293Sedstatic MALLOC_DEFINE(M_SMU, "smu", "SMU Sensor Information");
215194679Snwhitehorn
216204082Snwhitehorn#define SMU_MAILBOX		0x8000860c
217204692Snwhitehorn#define SMU_FANMGT_INTERVAL	1000 /* ms */
218194679Snwhitehorn
219194679Snwhitehorn/* Command types */
220204082Snwhitehorn#define SMU_ADC			0xd8
221204082Snwhitehorn#define SMU_FAN			0x4a
222232482Sandreast#define SMU_RPM_STATUS		0x01
223232482Sandreast#define SMU_RPM_SETPOINT	0x02
224232482Sandreast#define SMU_PWM_STATUS		0x11
225232482Sandreast#define SMU_PWM_SETPOINT	0x12
226204082Snwhitehorn#define SMU_I2C			0x9a
227204179Snwhitehorn#define  SMU_I2C_SIMPLE		0x00
228204179Snwhitehorn#define  SMU_I2C_NORMAL		0x01
229204179Snwhitehorn#define  SMU_I2C_COMBINED	0x02
230204082Snwhitehorn#define SMU_MISC		0xee
231204179Snwhitehorn#define  SMU_MISC_GET_DATA	0x02
232204179Snwhitehorn#define  SMU_MISC_LED_CTRL	0x04
233204082Snwhitehorn#define SMU_POWER		0xaa
234204270Snwhitehorn#define SMU_POWER_EVENTS	0x8f
235204270Snwhitehorn#define  SMU_PWR_GET_POWERUP	0x00
236204270Snwhitehorn#define  SMU_PWR_SET_POWERUP	0x01
237204270Snwhitehorn#define  SMU_PWR_CLR_POWERUP	0x02
238205506Snwhitehorn#define SMU_RTC			0x8e
239205506Snwhitehorn#define  SMU_RTC_GET		0x81
240205506Snwhitehorn#define  SMU_RTC_SET		0x80
241194679Snwhitehorn
242204270Snwhitehorn/* Power event types */
243204270Snwhitehorn#define SMU_WAKEUP_KEYPRESS	0x01
244204270Snwhitehorn#define SMU_WAKEUP_AC_INSERT	0x02
245204270Snwhitehorn#define SMU_WAKEUP_AC_CHANGE	0x04
246204270Snwhitehorn#define SMU_WAKEUP_RING		0x10
247204270Snwhitehorn
248204082Snwhitehorn/* Data blocks */
249204082Snwhitehorn#define SMU_CPUTEMP_CAL		0x18
250204082Snwhitehorn#define SMU_CPUVOLT_CAL		0x21
251204082Snwhitehorn#define SMU_SLOTPW_CAL		0x78
252204082Snwhitehorn
253204082Snwhitehorn/* Partitions */
254204082Snwhitehorn#define SMU_PARTITION		0x3e
255204082Snwhitehorn#define SMU_PARTITION_LATEST	0x01
256204082Snwhitehorn#define SMU_PARTITION_BASE	0x02
257204082Snwhitehorn#define SMU_PARTITION_UPDATE	0x03
258204082Snwhitehorn
259194679Snwhitehornstatic int
260194679Snwhitehornsmu_probe(device_t dev)
261194679Snwhitehorn{
262194679Snwhitehorn	const char *name = ofw_bus_get_name(dev);
263194679Snwhitehorn
264194679Snwhitehorn	if (strcmp(name, "smu") != 0)
265194679Snwhitehorn		return (ENXIO);
266194679Snwhitehorn
267194679Snwhitehorn	device_set_desc(dev, "Apple System Management Unit");
268194679Snwhitehorn	return (0);
269194679Snwhitehorn}
270194679Snwhitehorn
271194679Snwhitehornstatic void
272194679Snwhitehornsmu_phys_callback(void *xsc, bus_dma_segment_t *segs, int nsegs, int error)
273194679Snwhitehorn{
274194679Snwhitehorn	struct smu_softc *sc = xsc;
275194679Snwhitehorn
276194679Snwhitehorn	sc->sc_cmd_phys = segs[0].ds_addr;
277194679Snwhitehorn}
278194679Snwhitehorn
279194679Snwhitehornstatic int
280194679Snwhitehornsmu_attach(device_t dev)
281194679Snwhitehorn{
282194679Snwhitehorn	struct smu_softc *sc;
283204082Snwhitehorn	phandle_t	node, child;
284204082Snwhitehorn	uint8_t		data[12];
285194679Snwhitehorn
286194679Snwhitehorn	sc = device_get_softc(dev);
287194679Snwhitehorn
288194679Snwhitehorn	mtx_init(&sc->sc_mtx, "smu", NULL, MTX_DEF);
289204692Snwhitehorn	sc->sc_cur_cmd = NULL;
290204692Snwhitehorn	sc->sc_doorbellirqid = -1;
291194679Snwhitehorn
292215100Snwhitehorn	sc->sc_u3 = 0;
293215100Snwhitehorn	if (OF_finddevice("/u3") != -1)
294215100Snwhitehorn		sc->sc_u3 = 1;
295215100Snwhitehorn
296194679Snwhitehorn	/*
297194679Snwhitehorn	 * Map the mailbox area. This should be determined from firmware,
298194679Snwhitehorn	 * but I have not found a simple way to do that.
299194679Snwhitehorn	 */
300194679Snwhitehorn	bus_dma_tag_create(NULL, 16, 0, BUS_SPACE_MAXADDR_32BIT,
301194679Snwhitehorn	    BUS_SPACE_MAXADDR, NULL, NULL, PAGE_SIZE, 1, PAGE_SIZE, 0, NULL,
302194679Snwhitehorn	    NULL, &(sc->sc_dmatag));
303204082Snwhitehorn	sc->sc_bt = &bs_le_tag;
304194679Snwhitehorn	bus_space_map(sc->sc_bt, SMU_MAILBOX, 4, 0, &sc->sc_mailbox);
305194679Snwhitehorn
306194679Snwhitehorn	/*
307194679Snwhitehorn	 * Allocate the command buffer. This can be anywhere in the low 4 GB
308194679Snwhitehorn	 * of memory.
309194679Snwhitehorn	 */
310194679Snwhitehorn	bus_dmamem_alloc(sc->sc_dmatag, (void **)&sc->sc_cmd, BUS_DMA_WAITOK |
311194679Snwhitehorn	    BUS_DMA_ZERO, &sc->sc_cmd_dmamap);
312194679Snwhitehorn	bus_dmamap_load(sc->sc_dmatag, sc->sc_cmd_dmamap,
313194679Snwhitehorn	    sc->sc_cmd, PAGE_SIZE, smu_phys_callback, sc, 0);
314204692Snwhitehorn	STAILQ_INIT(&sc->sc_cmdq);
315194679Snwhitehorn
316194679Snwhitehorn	/*
317194679Snwhitehorn	 * Set up handlers to change CPU voltage when CPU frequency is changed.
318194679Snwhitehorn	 */
319194679Snwhitehorn	EVENTHANDLER_REGISTER(cpufreq_pre_change, smu_cpufreq_pre_change, dev,
320194679Snwhitehorn	    EVENTHANDLER_PRI_ANY);
321194679Snwhitehorn	EVENTHANDLER_REGISTER(cpufreq_post_change, smu_cpufreq_post_change, dev,
322194679Snwhitehorn	    EVENTHANDLER_PRI_ANY);
323194679Snwhitehorn
324232482Sandreast	node = ofw_bus_get_node(dev);
325232482Sandreast
326232482Sandreast	/* Some SMUs have RPM and PWM controlled fans which do not sit
327232482Sandreast	 * under the same node. So we have to attach them separately.
328232482Sandreast	 */
329232482Sandreast	smu_attach_fans(dev, node);
330232482Sandreast
331204082Snwhitehorn	/*
332232482Sandreast	 * Now detect and attach the other child devices.
333204082Snwhitehorn	 */
334204082Snwhitehorn	for (child = OF_child(node); child != 0; child = OF_peer(child)) {
335204082Snwhitehorn		char name[32];
336204082Snwhitehorn		memset(name, 0, sizeof(name));
337204082Snwhitehorn		OF_getprop(child, "name", name, sizeof(name));
338204082Snwhitehorn
339204082Snwhitehorn		if (strncmp(name, "sensors", 8) == 0)
340204082Snwhitehorn			smu_attach_sensors(dev, child);
341208841Snwhitehorn
342208841Snwhitehorn		if (strncmp(name, "smu-i2c-control", 15) == 0)
343208841Snwhitehorn			smu_attach_i2c(dev, child);
344204082Snwhitehorn	}
345204082Snwhitehorn
346208841Snwhitehorn	/* Some SMUs have the I2C children directly under the bus. */
347208841Snwhitehorn	smu_attach_i2c(dev, node);
348208841Snwhitehorn
349204082Snwhitehorn	/*
350204082Snwhitehorn	 * Collect calibration constants.
351204082Snwhitehorn	 */
352204082Snwhitehorn	smu_get_datablock(dev, SMU_CPUTEMP_CAL, data, sizeof(data));
353204082Snwhitehorn	sc->sc_cpu_diode_scale = (data[4] << 8) + data[5];
354204082Snwhitehorn	sc->sc_cpu_diode_offset = (data[6] << 8) + data[7];
355204082Snwhitehorn
356204082Snwhitehorn	smu_get_datablock(dev, SMU_CPUVOLT_CAL, data, sizeof(data));
357204082Snwhitehorn	sc->sc_cpu_volt_scale = (data[4] << 8) + data[5];
358204082Snwhitehorn	sc->sc_cpu_volt_offset = (data[6] << 8) + data[7];
359204082Snwhitehorn	sc->sc_cpu_curr_scale = (data[8] << 8) + data[9];
360204082Snwhitehorn	sc->sc_cpu_curr_offset = (data[10] << 8) + data[11];
361204082Snwhitehorn
362204082Snwhitehorn	smu_get_datablock(dev, SMU_SLOTPW_CAL, data, sizeof(data));
363204082Snwhitehorn	sc->sc_slots_pow_scale = (data[4] << 8) + data[5];
364204082Snwhitehorn	sc->sc_slots_pow_offset = (data[6] << 8) + data[7];
365204082Snwhitehorn
366204180Snwhitehorn	/*
367204218Snwhitehorn	 * Set up LED interface
368204218Snwhitehorn	 */
369204218Snwhitehorn	sc->sc_leddev = led_create(smu_set_sleepled, dev, "sleepled");
370204218Snwhitehorn
371204270Snwhitehorn	/*
372204270Snwhitehorn	 * Reset on power loss behavior
373204270Snwhitehorn	 */
374204270Snwhitehorn
375204270Snwhitehorn	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
376204270Snwhitehorn            SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
377204270Snwhitehorn	    "server_mode", CTLTYPE_INT | CTLFLAG_RW, dev, 0,
378204270Snwhitehorn	    smu_server_mode, "I", "Enable reboot after power failure");
379204270Snwhitehorn
380204692Snwhitehorn	/*
381204692Snwhitehorn	 * Set up doorbell interrupt.
382204692Snwhitehorn	 */
383204692Snwhitehorn	sc->sc_doorbellirqid = 0;
384204692Snwhitehorn	sc->sc_doorbellirq = bus_alloc_resource_any(smu_doorbell, SYS_RES_IRQ,
385204692Snwhitehorn	    &sc->sc_doorbellirqid, RF_ACTIVE);
386204692Snwhitehorn	bus_setup_intr(smu_doorbell, sc->sc_doorbellirq,
387204692Snwhitehorn	    INTR_TYPE_MISC | INTR_MPSAFE, NULL, smu_doorbell_intr, dev,
388204692Snwhitehorn	    &sc->sc_doorbellirqcookie);
389204692Snwhitehorn	powerpc_config_intr(rman_get_start(sc->sc_doorbellirq),
390204692Snwhitehorn	    INTR_TRIGGER_EDGE, INTR_POLARITY_LOW);
391204692Snwhitehorn
392205506Snwhitehorn	/*
393205506Snwhitehorn	 * Connect RTC interface.
394205506Snwhitehorn	 */
395205506Snwhitehorn	clock_register(dev, 1000);
396205506Snwhitehorn
397212054Snwhitehorn	/*
398212054Snwhitehorn	 * Learn about shutdown events
399212054Snwhitehorn	 */
400212054Snwhitehorn	EVENTHANDLER_REGISTER(shutdown_final, smu_shutdown, dev,
401212054Snwhitehorn	    SHUTDOWN_PRI_LAST);
402212054Snwhitehorn
403208841Snwhitehorn	return (bus_generic_attach(dev));
404194679Snwhitehorn}
405194679Snwhitehorn
406208841Snwhitehornstatic const struct ofw_bus_devinfo *
407208841Snwhitehornsmu_get_devinfo(device_t bus, device_t dev)
408208841Snwhitehorn{
409208841Snwhitehorn
410208841Snwhitehorn	return (device_get_ivars(dev));
411208841Snwhitehorn}
412208841Snwhitehorn
413204692Snwhitehornstatic void
414204692Snwhitehornsmu_send_cmd(device_t dev, struct smu_cmd *cmd)
415194679Snwhitehorn{
416194679Snwhitehorn	struct smu_softc *sc;
417194679Snwhitehorn
418194679Snwhitehorn	sc = device_get_softc(dev);
419194679Snwhitehorn
420204692Snwhitehorn	mtx_assert(&sc->sc_mtx, MA_OWNED);
421194679Snwhitehorn
422215100Snwhitehorn	if (sc->sc_u3)
423215100Snwhitehorn		powerpc_pow_enabled = 0; /* SMU cannot work if we go to NAP */
424215100Snwhitehorn
425204692Snwhitehorn	sc->sc_cur_cmd = cmd;
426204082Snwhitehorn
427194679Snwhitehorn	/* Copy the command to the mailbox */
428204692Snwhitehorn	sc->sc_cmd->cmd = cmd->cmd;
429204692Snwhitehorn	sc->sc_cmd->len = cmd->len;
430204692Snwhitehorn	memcpy(sc->sc_cmd->data, cmd->data, sizeof(cmd->data));
431194679Snwhitehorn	bus_dmamap_sync(sc->sc_dmatag, sc->sc_cmd_dmamap, BUS_DMASYNC_PREWRITE);
432194679Snwhitehorn	bus_space_write_4(sc->sc_bt, sc->sc_mailbox, 0, sc->sc_cmd_phys);
433194679Snwhitehorn
434204082Snwhitehorn	/* Flush the cacheline it is in -- SMU bypasses the cache */
435204082Snwhitehorn	__asm __volatile("sync; dcbf 0,%0; sync" :: "r"(sc->sc_cmd): "memory");
436194679Snwhitehorn
437194679Snwhitehorn	/* Ring SMU doorbell */
438194679Snwhitehorn	macgpio_write(smu_doorbell, GPIO_DDR_OUTPUT);
439204692Snwhitehorn}
440194679Snwhitehorn
441204692Snwhitehornstatic void
442204692Snwhitehornsmu_doorbell_intr(void *xdev)
443204692Snwhitehorn{
444204692Snwhitehorn	device_t smu;
445204692Snwhitehorn	struct smu_softc *sc;
446204692Snwhitehorn	int doorbell_ack;
447194679Snwhitehorn
448204692Snwhitehorn	smu = xdev;
449204692Snwhitehorn	doorbell_ack = macgpio_read(smu_doorbell);
450204692Snwhitehorn	sc = device_get_softc(smu);
451204692Snwhitehorn
452204692Snwhitehorn	if (doorbell_ack != (GPIO_DDR_OUTPUT | GPIO_LEVEL_RO | GPIO_DATA))
453204692Snwhitehorn		return;
454204692Snwhitehorn
455204692Snwhitehorn	mtx_lock(&sc->sc_mtx);
456204692Snwhitehorn
457204692Snwhitehorn	if (sc->sc_cur_cmd == NULL)	/* spurious */
458204692Snwhitehorn		goto done;
459204692Snwhitehorn
460194679Snwhitehorn	/* Check result. First invalidate the cache again... */
461194679Snwhitehorn	__asm __volatile("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory");
462194679Snwhitehorn
463194679Snwhitehorn	bus_dmamap_sync(sc->sc_dmatag, sc->sc_cmd_dmamap, BUS_DMASYNC_POSTREAD);
464194679Snwhitehorn
465204692Snwhitehorn	sc->sc_cur_cmd->cmd = sc->sc_cmd->cmd;
466204692Snwhitehorn	sc->sc_cur_cmd->len = sc->sc_cmd->len;
467204692Snwhitehorn	memcpy(sc->sc_cur_cmd->data, sc->sc_cmd->data,
468204692Snwhitehorn	    sizeof(sc->sc_cmd->data));
469204692Snwhitehorn	wakeup(sc->sc_cur_cmd);
470204692Snwhitehorn	sc->sc_cur_cmd = NULL;
471215100Snwhitehorn	if (sc->sc_u3)
472215100Snwhitehorn		powerpc_pow_enabled = 1;
473194679Snwhitehorn
474204692Snwhitehorn    done:
475204692Snwhitehorn	/* Queue next command if one is pending */
476204692Snwhitehorn	if (STAILQ_FIRST(&sc->sc_cmdq) != NULL) {
477204692Snwhitehorn		sc->sc_cur_cmd = STAILQ_FIRST(&sc->sc_cmdq);
478204692Snwhitehorn		STAILQ_REMOVE_HEAD(&sc->sc_cmdq, cmd_q);
479204692Snwhitehorn		smu_send_cmd(smu, sc->sc_cur_cmd);
480204692Snwhitehorn	}
481204082Snwhitehorn
482204692Snwhitehorn	mtx_unlock(&sc->sc_mtx);
483204692Snwhitehorn}
484204082Snwhitehorn
485204692Snwhitehornstatic int
486204692Snwhitehornsmu_run_cmd(device_t dev, struct smu_cmd *cmd, int wait)
487204692Snwhitehorn{
488204692Snwhitehorn	struct smu_softc *sc;
489204692Snwhitehorn	uint8_t cmd_code;
490204692Snwhitehorn	int error;
491204692Snwhitehorn
492204692Snwhitehorn	sc = device_get_softc(dev);
493204692Snwhitehorn	cmd_code = cmd->cmd;
494204692Snwhitehorn
495204692Snwhitehorn	mtx_lock(&sc->sc_mtx);
496204692Snwhitehorn	if (sc->sc_cur_cmd != NULL) {
497204692Snwhitehorn		STAILQ_INSERT_TAIL(&sc->sc_cmdq, cmd, cmd_q);
498204692Snwhitehorn	} else
499204692Snwhitehorn		smu_send_cmd(dev, cmd);
500194679Snwhitehorn	mtx_unlock(&sc->sc_mtx);
501194679Snwhitehorn
502204692Snwhitehorn	if (!wait)
503204692Snwhitehorn		return (0);
504204692Snwhitehorn
505204692Snwhitehorn	if (sc->sc_doorbellirqid < 0) {
506204692Snwhitehorn		/* Poll if the IRQ has not been set up yet */
507204692Snwhitehorn		do {
508204692Snwhitehorn			DELAY(50);
509204692Snwhitehorn			smu_doorbell_intr(dev);
510204692Snwhitehorn		} while (sc->sc_cur_cmd != NULL);
511204692Snwhitehorn	} else {
512204692Snwhitehorn		/* smu_doorbell_intr will wake us when the command is ACK'ed */
513204692Snwhitehorn		error = tsleep(cmd, 0, "smu", 800 * hz / 1000);
514204692Snwhitehorn		if (error != 0)
515204692Snwhitehorn			smu_doorbell_intr(dev);	/* One last chance */
516204692Snwhitehorn
517204692Snwhitehorn		if (error != 0) {
518204692Snwhitehorn		    mtx_lock(&sc->sc_mtx);
519204692Snwhitehorn		    if (cmd->cmd == cmd_code) {	/* Never processed */
520204692Snwhitehorn			/* Abort this command if we timed out */
521204692Snwhitehorn			if (sc->sc_cur_cmd == cmd)
522204692Snwhitehorn				sc->sc_cur_cmd = NULL;
523204692Snwhitehorn			else
524204692Snwhitehorn				STAILQ_REMOVE(&sc->sc_cmdq, cmd, smu_cmd,
525204692Snwhitehorn				    cmd_q);
526204692Snwhitehorn			mtx_unlock(&sc->sc_mtx);
527204692Snwhitehorn			return (error);
528204692Snwhitehorn		    }
529204692Snwhitehorn		    error = 0;
530204692Snwhitehorn		    mtx_unlock(&sc->sc_mtx);
531204692Snwhitehorn		}
532204692Snwhitehorn	}
533204692Snwhitehorn
534204692Snwhitehorn	/* SMU acks the command by inverting the command bits */
535204692Snwhitehorn	if (cmd->cmd == ((~cmd_code) & 0xff))
536204692Snwhitehorn		error = 0;
537204692Snwhitehorn	else
538204692Snwhitehorn		error = EIO;
539204692Snwhitehorn
540204692Snwhitehorn	return (error);
541194679Snwhitehorn}
542194679Snwhitehorn
543204082Snwhitehornstatic int
544204082Snwhitehornsmu_get_datablock(device_t dev, int8_t id, uint8_t *buf, size_t len)
545204082Snwhitehorn{
546204082Snwhitehorn	struct smu_cmd cmd;
547204082Snwhitehorn	uint8_t addr[4];
548204082Snwhitehorn
549204082Snwhitehorn	cmd.cmd = SMU_PARTITION;
550204082Snwhitehorn	cmd.len = 2;
551204082Snwhitehorn	cmd.data[0] = SMU_PARTITION_LATEST;
552204082Snwhitehorn	cmd.data[1] = id;
553204082Snwhitehorn
554204692Snwhitehorn	smu_run_cmd(dev, &cmd, 1);
555204082Snwhitehorn
556204082Snwhitehorn	addr[0] = addr[1] = 0;
557204082Snwhitehorn	addr[2] = cmd.data[0];
558204082Snwhitehorn	addr[3] = cmd.data[1];
559204082Snwhitehorn
560204082Snwhitehorn	cmd.cmd = SMU_MISC;
561204082Snwhitehorn	cmd.len = 7;
562204082Snwhitehorn	cmd.data[0] = SMU_MISC_GET_DATA;
563204082Snwhitehorn	cmd.data[1] = sizeof(addr);
564204082Snwhitehorn	memcpy(&cmd.data[2], addr, sizeof(addr));
565204082Snwhitehorn	cmd.data[6] = len;
566204082Snwhitehorn
567204692Snwhitehorn	smu_run_cmd(dev, &cmd, 1);
568204082Snwhitehorn	memcpy(buf, cmd.data, len);
569204082Snwhitehorn	return (0);
570204082Snwhitehorn}
571204082Snwhitehorn
572194679Snwhitehornstatic void
573194679Snwhitehornsmu_slew_cpu_voltage(device_t dev, int to)
574194679Snwhitehorn{
575194679Snwhitehorn	struct smu_cmd cmd;
576194679Snwhitehorn
577194679Snwhitehorn	cmd.cmd = SMU_POWER;
578194679Snwhitehorn	cmd.len = 8;
579194679Snwhitehorn	cmd.data[0] = 'V';
580194679Snwhitehorn	cmd.data[1] = 'S';
581194679Snwhitehorn	cmd.data[2] = 'L';
582194679Snwhitehorn	cmd.data[3] = 'E';
583194679Snwhitehorn	cmd.data[4] = 'W';
584194679Snwhitehorn	cmd.data[5] = 0xff;
585194679Snwhitehorn	cmd.data[6] = 1;
586194679Snwhitehorn	cmd.data[7] = to;
587194679Snwhitehorn
588204692Snwhitehorn	smu_run_cmd(dev, &cmd, 1);
589194679Snwhitehorn}
590194679Snwhitehorn
591194679Snwhitehornstatic void
592194679Snwhitehornsmu_cpufreq_pre_change(device_t dev, const struct cf_level *level)
593194679Snwhitehorn{
594194679Snwhitehorn	/*
595194679Snwhitehorn	 * Make sure the CPU voltage is raised before we raise
596194679Snwhitehorn	 * the clock.
597194679Snwhitehorn	 */
598194679Snwhitehorn
599194679Snwhitehorn	if (level->rel_set[0].freq == 10000 /* max */)
600194679Snwhitehorn		smu_slew_cpu_voltage(dev, 0);
601194679Snwhitehorn}
602194679Snwhitehorn
603194679Snwhitehornstatic void
604194679Snwhitehornsmu_cpufreq_post_change(device_t dev, const struct cf_level *level)
605194679Snwhitehorn{
606194679Snwhitehorn	/* We are safe to reduce CPU voltage after a downward transition */
607194679Snwhitehorn
608194679Snwhitehorn	if (level->rel_set[0].freq < 10000 /* max */)
609194679Snwhitehorn		smu_slew_cpu_voltage(dev, 1); /* XXX: 1/4 voltage for 970MP? */
610194679Snwhitehorn}
611194679Snwhitehorn
612194679Snwhitehorn/* Routines for probing the SMU doorbell GPIO */
613194679Snwhitehornstatic int doorbell_probe(device_t dev);
614194679Snwhitehornstatic int doorbell_attach(device_t dev);
615194679Snwhitehorn
616194679Snwhitehornstatic device_method_t  doorbell_methods[] = {
617194679Snwhitehorn	/* Device interface */
618194679Snwhitehorn	DEVMETHOD(device_probe,		doorbell_probe),
619194679Snwhitehorn	DEVMETHOD(device_attach,	doorbell_attach),
620194679Snwhitehorn	{ 0, 0 },
621194679Snwhitehorn};
622194679Snwhitehorn
623194679Snwhitehornstatic driver_t doorbell_driver = {
624194679Snwhitehorn	"smudoorbell",
625194679Snwhitehorn	doorbell_methods,
626194679Snwhitehorn	0
627194679Snwhitehorn};
628194679Snwhitehorn
629194679Snwhitehornstatic devclass_t doorbell_devclass;
630194679Snwhitehorn
631194679SnwhitehornDRIVER_MODULE(smudoorbell, macgpio, doorbell_driver, doorbell_devclass, 0, 0);
632194679Snwhitehorn
633194679Snwhitehornstatic int
634194679Snwhitehorndoorbell_probe(device_t dev)
635194679Snwhitehorn{
636194679Snwhitehorn	const char *name = ofw_bus_get_name(dev);
637194679Snwhitehorn
638194679Snwhitehorn	if (strcmp(name, "smu-doorbell") != 0)
639194679Snwhitehorn		return (ENXIO);
640194679Snwhitehorn
641194679Snwhitehorn	device_set_desc(dev, "SMU Doorbell GPIO");
642194679Snwhitehorn	device_quiet(dev);
643194679Snwhitehorn	return (0);
644194679Snwhitehorn}
645194679Snwhitehorn
646194679Snwhitehornstatic int
647194679Snwhitehorndoorbell_attach(device_t dev)
648194679Snwhitehorn{
649194679Snwhitehorn	smu_doorbell = dev;
650194679Snwhitehorn	return (0);
651194679Snwhitehorn}
652204082Snwhitehorn
653204082Snwhitehorn/*
654204082Snwhitehorn * Sensor and fan management
655204082Snwhitehorn */
656204082Snwhitehorn
657204082Snwhitehornstatic int
658250290Snwhitehornsmu_fan_check_old_style(struct smu_fan *fan)
659250290Snwhitehorn{
660250290Snwhitehorn	device_t smu = fan->dev;
661250290Snwhitehorn	struct smu_softc *sc = device_get_softc(smu);
662250290Snwhitehorn	struct smu_cmd cmd;
663250290Snwhitehorn	int error;
664250290Snwhitehorn
665250290Snwhitehorn	if (sc->old_style_fans != -1)
666250290Snwhitehorn		return (sc->old_style_fans);
667250290Snwhitehorn
668250290Snwhitehorn	/*
669250290Snwhitehorn	 * Apple has two fan control mechanisms. We can't distinguish
670250290Snwhitehorn	 * them except by seeing if the new one fails. If the new one
671250290Snwhitehorn	 * fails, use the old one.
672250290Snwhitehorn	 */
673250290Snwhitehorn
674250290Snwhitehorn	cmd.cmd = SMU_FAN;
675250290Snwhitehorn	cmd.len = 2;
676250290Snwhitehorn	cmd.data[0] = 0x31;
677250290Snwhitehorn	cmd.data[1] = fan->reg;
678250290Snwhitehorn
679250290Snwhitehorn	do {
680250290Snwhitehorn		error = smu_run_cmd(smu, &cmd, 1);
681250290Snwhitehorn	} while (error == EWOULDBLOCK);
682250290Snwhitehorn
683250290Snwhitehorn	sc->old_style_fans = (error != 0);
684250290Snwhitehorn
685250290Snwhitehorn	return (sc->old_style_fans);
686250290Snwhitehorn}
687250290Snwhitehorn
688250290Snwhitehornstatic int
689222429Snwhitehornsmu_fan_set_rpm(struct smu_fan *fan, int rpm)
690204082Snwhitehorn{
691222429Snwhitehorn	device_t smu = fan->dev;
692204082Snwhitehorn	struct smu_cmd cmd;
693204179Snwhitehorn	int error;
694204082Snwhitehorn
695204082Snwhitehorn	cmd.cmd = SMU_FAN;
696204179Snwhitehorn	error = EIO;
697204082Snwhitehorn
698204179Snwhitehorn	/* Clamp to allowed range */
699222429Snwhitehorn	rpm = max(fan->fan.min_rpm, rpm);
700222429Snwhitehorn	rpm = min(fan->fan.max_rpm, rpm);
701204179Snwhitehorn
702250290Snwhitehorn	smu_fan_check_old_style(fan);
703250290Snwhitehorn
704204179Snwhitehorn	if (!fan->old_style) {
705204179Snwhitehorn		cmd.len = 4;
706204179Snwhitehorn		cmd.data[0] = 0x30;
707204179Snwhitehorn		cmd.data[1] = fan->reg;
708204179Snwhitehorn		cmd.data[2] = (rpm >> 8) & 0xff;
709204179Snwhitehorn		cmd.data[3] = rpm & 0xff;
710232482Sandreast
711204692Snwhitehorn		error = smu_run_cmd(smu, &cmd, 1);
712222430Snwhitehorn		if (error && error != EWOULDBLOCK)
713204179Snwhitehorn			fan->old_style = 1;
714250290Snwhitehorn	} else {
715204179Snwhitehorn		cmd.len = 14;
716232482Sandreast		cmd.data[0] = 0x00; /* RPM fan. */
717204179Snwhitehorn		cmd.data[1] = 1 << fan->reg;
718204179Snwhitehorn		cmd.data[2 + 2*fan->reg] = (rpm >> 8) & 0xff;
719204179Snwhitehorn		cmd.data[3 + 2*fan->reg] = rpm & 0xff;
720204692Snwhitehorn		error = smu_run_cmd(smu, &cmd, 1);
721204179Snwhitehorn	}
722204082Snwhitehorn
723204180Snwhitehorn	if (error == 0)
724204180Snwhitehorn		fan->setpoint = rpm;
725204180Snwhitehorn
726204179Snwhitehorn	return (error);
727204082Snwhitehorn}
728204082Snwhitehorn
729204082Snwhitehornstatic int
730222429Snwhitehornsmu_fan_read_rpm(struct smu_fan *fan)
731204082Snwhitehorn{
732222429Snwhitehorn	device_t smu = fan->dev;
733204082Snwhitehorn	struct smu_cmd cmd;
734208167Snwhitehorn	int rpm, error;
735204082Snwhitehorn
736250290Snwhitehorn	smu_fan_check_old_style(fan);
737250290Snwhitehorn
738208167Snwhitehorn	if (!fan->old_style) {
739208167Snwhitehorn		cmd.cmd = SMU_FAN;
740208167Snwhitehorn		cmd.len = 2;
741208167Snwhitehorn		cmd.data[0] = 0x31;
742208167Snwhitehorn		cmd.data[1] = fan->reg;
743204082Snwhitehorn
744208167Snwhitehorn		error = smu_run_cmd(smu, &cmd, 1);
745222430Snwhitehorn		if (error && error != EWOULDBLOCK)
746208167Snwhitehorn			fan->old_style = 1;
747204082Snwhitehorn
748208167Snwhitehorn		rpm = (cmd.data[0] << 8) | cmd.data[1];
749208167Snwhitehorn	}
750208167Snwhitehorn
751208167Snwhitehorn	if (fan->old_style) {
752208167Snwhitehorn		cmd.cmd = SMU_FAN;
753208167Snwhitehorn		cmd.len = 1;
754232482Sandreast		cmd.data[0] = SMU_RPM_STATUS;
755208167Snwhitehorn
756208167Snwhitehorn		error = smu_run_cmd(smu, &cmd, 1);
757208167Snwhitehorn		if (error)
758208167Snwhitehorn			return (error);
759208167Snwhitehorn
760208167Snwhitehorn		rpm = (cmd.data[fan->reg*2+1] << 8) | cmd.data[fan->reg*2+2];
761208167Snwhitehorn	}
762208167Snwhitehorn
763208167Snwhitehorn	return (rpm);
764204082Snwhitehorn}
765232482Sandreaststatic int
766232482Sandreastsmu_fan_set_pwm(struct smu_fan *fan, int pwm)
767232482Sandreast{
768232482Sandreast	device_t smu = fan->dev;
769232482Sandreast	struct smu_cmd cmd;
770232482Sandreast	int error;
771204082Snwhitehorn
772232482Sandreast	cmd.cmd = SMU_FAN;
773232482Sandreast	error = EIO;
774232482Sandreast
775232482Sandreast	/* Clamp to allowed range */
776232482Sandreast	pwm = max(fan->fan.min_rpm, pwm);
777232482Sandreast	pwm = min(fan->fan.max_rpm, pwm);
778232482Sandreast
779232482Sandreast	/*
780232482Sandreast	 * Apple has two fan control mechanisms. We can't distinguish
781232482Sandreast	 * them except by seeing if the new one fails. If the new one
782232482Sandreast	 * fails, use the old one.
783232482Sandreast	 */
784232482Sandreast
785232482Sandreast	if (!fan->old_style) {
786232482Sandreast		cmd.len = 4;
787232482Sandreast		cmd.data[0] = 0x30;
788232482Sandreast		cmd.data[1] = fan->reg;
789232482Sandreast		cmd.data[2] = (pwm >> 8) & 0xff;
790232482Sandreast		cmd.data[3] = pwm & 0xff;
791232482Sandreast
792232482Sandreast		error = smu_run_cmd(smu, &cmd, 1);
793232482Sandreast		if (error && error != EWOULDBLOCK)
794232482Sandreast			fan->old_style = 1;
795232482Sandreast	}
796232482Sandreast
797232482Sandreast	if (fan->old_style) {
798232482Sandreast		cmd.len = 14;
799232482Sandreast		cmd.data[0] = 0x10; /* PWM fan. */
800232482Sandreast		cmd.data[1] = 1 << fan->reg;
801232482Sandreast		cmd.data[2 + 2*fan->reg] = (pwm >> 8) & 0xff;
802232482Sandreast		cmd.data[3 + 2*fan->reg] = pwm & 0xff;
803232482Sandreast		error = smu_run_cmd(smu, &cmd, 1);
804232482Sandreast	}
805232482Sandreast
806232482Sandreast	if (error == 0)
807232482Sandreast		fan->setpoint = pwm;
808232482Sandreast
809232482Sandreast	return (error);
810232482Sandreast}
811232482Sandreast
812204082Snwhitehornstatic int
813232482Sandreastsmu_fan_read_pwm(struct smu_fan *fan, int *pwm, int *rpm)
814232482Sandreast{
815232482Sandreast	device_t smu = fan->dev;
816232482Sandreast	struct smu_cmd cmd;
817232482Sandreast	int error;
818232482Sandreast
819232482Sandreast	if (!fan->old_style) {
820232482Sandreast		cmd.cmd = SMU_FAN;
821232482Sandreast		cmd.len = 2;
822232482Sandreast		cmd.data[0] = 0x31;
823232482Sandreast		cmd.data[1] = fan->reg;
824232482Sandreast
825232482Sandreast		error = smu_run_cmd(smu, &cmd, 1);
826232482Sandreast		if (error && error != EWOULDBLOCK)
827232482Sandreast			fan->old_style = 1;
828232482Sandreast
829232482Sandreast		*rpm = (cmd.data[0] << 8) | cmd.data[1];
830232482Sandreast	}
831232482Sandreast
832232482Sandreast	if (fan->old_style) {
833232482Sandreast		cmd.cmd = SMU_FAN;
834232482Sandreast		cmd.len = 1;
835232482Sandreast		cmd.data[0] = SMU_PWM_STATUS;
836232482Sandreast
837232482Sandreast		error = smu_run_cmd(smu, &cmd, 1);
838232482Sandreast		if (error)
839232482Sandreast			return (error);
840232482Sandreast
841232482Sandreast		*rpm = (cmd.data[fan->reg*2+1] << 8) | cmd.data[fan->reg*2+2];
842232482Sandreast	}
843232482Sandreast	if (fan->old_style) {
844232482Sandreast		cmd.cmd = SMU_FAN;
845232482Sandreast		cmd.len = 14;
846232482Sandreast		cmd.data[0] = SMU_PWM_SETPOINT;
847232482Sandreast		cmd.data[1] = 1 << fan->reg;
848232482Sandreast
849232482Sandreast		error = smu_run_cmd(smu, &cmd, 1);
850232482Sandreast		if (error)
851232482Sandreast			return (error);
852232482Sandreast
853232482Sandreast		*pwm = cmd.data[fan->reg*2+2];
854232482Sandreast	}
855232482Sandreast	return (0);
856232482Sandreast}
857232482Sandreast
858232482Sandreaststatic int
859204082Snwhitehornsmu_fanrpm_sysctl(SYSCTL_HANDLER_ARGS)
860204082Snwhitehorn{
861204082Snwhitehorn	device_t smu;
862204082Snwhitehorn	struct smu_softc *sc;
863204082Snwhitehorn	struct smu_fan *fan;
864232482Sandreast	int pwm = 0, rpm, error = 0;
865204082Snwhitehorn
866204082Snwhitehorn	smu = arg1;
867204082Snwhitehorn	sc = device_get_softc(smu);
868232482Sandreast	fan = &sc->sc_fans[arg2 & 0xff];
869204082Snwhitehorn
870232482Sandreast	if (fan->type == SMU_FAN_RPM) {
871232482Sandreast		rpm = smu_fan_read_rpm(fan);
872232482Sandreast		if (rpm < 0)
873232482Sandreast			return (rpm);
874208167Snwhitehorn
875232482Sandreast		error = sysctl_handle_int(oidp, &rpm, 0, req);
876232482Sandreast	} else {
877232482Sandreast		error = smu_fan_read_pwm(fan, &pwm, &rpm);
878232482Sandreast		if (error < 0)
879232482Sandreast			return (EIO);
880204082Snwhitehorn
881232482Sandreast		switch (arg2 & 0xff00) {
882232482Sandreast		case SMU_PWM_SYSCTL_PWM:
883232482Sandreast			error = sysctl_handle_int(oidp, &pwm, 0, req);
884232482Sandreast			break;
885232482Sandreast		case SMU_PWM_SYSCTL_RPM:
886232482Sandreast			error = sysctl_handle_int(oidp, &rpm, 0, req);
887232482Sandreast			break;
888232482Sandreast		default:
889232482Sandreast			/* This should never happen */
890232482Sandreast			return (EINVAL);
891297793Spfg		}
892232482Sandreast	}
893232482Sandreast	/* We can only read the RPM from a PWM controlled fan, so return. */
894232482Sandreast	if ((arg2 & 0xff00) == SMU_PWM_SYSCTL_RPM)
895232482Sandreast		return (0);
896232482Sandreast
897204082Snwhitehorn	if (error || !req->newptr)
898204082Snwhitehorn		return (error);
899204082Snwhitehorn
900204180Snwhitehorn	sc->sc_lastuserchange = time_uptime;
901204082Snwhitehorn
902232482Sandreast	if (fan->type == SMU_FAN_RPM)
903232482Sandreast		return (smu_fan_set_rpm(fan, rpm));
904232482Sandreast	else
905232482Sandreast		return (smu_fan_set_pwm(fan, pwm));
906204082Snwhitehorn}
907204082Snwhitehorn
908204082Snwhitehornstatic void
909232482Sandreastsmu_fill_fan_prop(device_t dev, phandle_t child, int id)
910232482Sandreast{
911232482Sandreast	struct smu_fan *fan;
912232482Sandreast	struct smu_softc *sc;
913232482Sandreast	char type[32];
914232482Sandreast
915232482Sandreast	sc = device_get_softc(dev);
916232482Sandreast	fan = &sc->sc_fans[id];
917232482Sandreast
918232482Sandreast	OF_getprop(child, "device_type", type, sizeof(type));
919232482Sandreast	/* We have either RPM or PWM controlled fans. */
920232482Sandreast	if (strcmp(type, "fan-rpm-control") == 0)
921232482Sandreast		fan->type = SMU_FAN_RPM;
922232482Sandreast	else
923232482Sandreast		fan->type = SMU_FAN_PWM;
924232482Sandreast
925232482Sandreast	fan->dev = dev;
926232482Sandreast	fan->old_style = 0;
927232482Sandreast	OF_getprop(child, "reg", &fan->reg,
928232482Sandreast		   sizeof(cell_t));
929232482Sandreast	OF_getprop(child, "min-value", &fan->fan.min_rpm,
930232482Sandreast		   sizeof(int));
931232482Sandreast	OF_getprop(child, "max-value", &fan->fan.max_rpm,
932232482Sandreast		   sizeof(int));
933232482Sandreast	OF_getprop(child, "zone", &fan->fan.zone,
934232482Sandreast		   sizeof(int));
935232482Sandreast
936232482Sandreast	if (OF_getprop(child, "unmanaged-value",
937232482Sandreast		       &fan->fan.default_rpm,
938232482Sandreast		       sizeof(int)) != sizeof(int))
939232482Sandreast		fan->fan.default_rpm = fan->fan.max_rpm;
940232482Sandreast
941232482Sandreast	OF_getprop(child, "location", fan->fan.name,
942232482Sandreast		   sizeof(fan->fan.name));
943232482Sandreast
944232482Sandreast	if (fan->type == SMU_FAN_RPM)
945232482Sandreast		fan->setpoint = smu_fan_read_rpm(fan);
946232482Sandreast	else
947232482Sandreast		smu_fan_read_pwm(fan, &fan->setpoint, &fan->rpm);
948232482Sandreast}
949232482Sandreast
950232482Sandreast/* On the first call count the number of fans. In the second call,
951232482Sandreast * after allocating the fan struct, fill the properties of the fans.
952232482Sandreast */
953232482Sandreaststatic int
954232482Sandreastsmu_count_fans(device_t dev)
955232482Sandreast{
956232482Sandreast	struct smu_softc *sc;
957232482Sandreast	phandle_t child, node, root;
958232482Sandreast	int nfans = 0;
959232482Sandreast
960232482Sandreast	node = ofw_bus_get_node(dev);
961232482Sandreast	sc = device_get_softc(dev);
962232482Sandreast
963232482Sandreast	/* First find the fanroots and count the number of fans. */
964232482Sandreast	for (root = OF_child(node); root != 0; root = OF_peer(root)) {
965232482Sandreast		char name[32];
966232482Sandreast		memset(name, 0, sizeof(name));
967232482Sandreast		OF_getprop(root, "name", name, sizeof(name));
968232482Sandreast		if (strncmp(name, "rpm-fans", 9) == 0 ||
969232482Sandreast		    strncmp(name, "pwm-fans", 9) == 0 ||
970232482Sandreast		    strncmp(name, "fans", 5) == 0)
971232482Sandreast			for (child = OF_child(root); child != 0;
972232482Sandreast			     child = OF_peer(child)) {
973232482Sandreast				nfans++;
974232482Sandreast				/* When allocated, fill the fan properties. */
975250290Snwhitehorn				if (sc->sc_fans != NULL) {
976232482Sandreast					smu_fill_fan_prop(dev, child,
977232482Sandreast							  nfans - 1);
978250290Snwhitehorn				}
979232482Sandreast			}
980232482Sandreast	}
981232482Sandreast	if (nfans == 0) {
982232482Sandreast		device_printf(dev, "WARNING: No fans detected!\n");
983232482Sandreast		return (0);
984232482Sandreast	}
985232482Sandreast	return (nfans);
986232482Sandreast}
987232482Sandreast
988232482Sandreaststatic void
989204082Snwhitehornsmu_attach_fans(device_t dev, phandle_t fanroot)
990204082Snwhitehorn{
991204082Snwhitehorn	struct smu_fan *fan;
992204082Snwhitehorn	struct smu_softc *sc;
993204082Snwhitehorn	struct sysctl_oid *oid, *fanroot_oid;
994204082Snwhitehorn	struct sysctl_ctx_list *ctx;
995232482Sandreast	char sysctl_name[32];
996232482Sandreast	int i, j;
997204082Snwhitehorn
998204082Snwhitehorn	sc = device_get_softc(dev);
999204082Snwhitehorn
1000232482Sandreast	/* Get the number of fans. */
1001232482Sandreast	sc->sc_nfans = smu_count_fans(dev);
1002232482Sandreast	if (sc->sc_nfans == 0)
1003204082Snwhitehorn		return;
1004204082Snwhitehorn
1005232482Sandreast	/* Now we're able to allocate memory for the fans struct. */
1006204082Snwhitehorn	sc->sc_fans = malloc(sc->sc_nfans * sizeof(struct smu_fan), M_SMU,
1007204082Snwhitehorn	    M_WAITOK | M_ZERO);
1008204082Snwhitehorn
1009232482Sandreast	/* Now fill in the properties. */
1010232482Sandreast	smu_count_fans(dev);
1011232482Sandreast
1012232482Sandreast	/* Register fans with pmac_thermal */
1013232482Sandreast	for (i = 0; i < sc->sc_nfans; i++)
1014232482Sandreast		pmac_thermal_fan_register(&sc->sc_fans[i].fan);
1015204082Snwhitehorn
1016204082Snwhitehorn	ctx = device_get_sysctl_ctx(dev);
1017204082Snwhitehorn	fanroot_oid = SYSCTL_ADD_NODE(ctx,
1018204082Snwhitehorn	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "fans",
1019204082Snwhitehorn	    CTLFLAG_RD, 0, "SMU Fan Information");
1020204082Snwhitehorn
1021232482Sandreast	/* Add sysctls */
1022232482Sandreast	for (i = 0; i < sc->sc_nfans; i++) {
1023232482Sandreast		fan = &sc->sc_fans[i];
1024232482Sandreast		for (j = 0; j < strlen(fan->fan.name); j++) {
1025232482Sandreast			sysctl_name[j] = tolower(fan->fan.name[j]);
1026232482Sandreast			if (isspace(sysctl_name[j]))
1027232482Sandreast				sysctl_name[j] = '_';
1028232482Sandreast		}
1029232482Sandreast		sysctl_name[j] = 0;
1030232482Sandreast		if (fan->type == SMU_FAN_RPM) {
1031232482Sandreast			oid = SYSCTL_ADD_NODE(ctx,
1032232482Sandreast					      SYSCTL_CHILDREN(fanroot_oid),
1033232482Sandreast					      OID_AUTO, sysctl_name,
1034232482Sandreast					      CTLFLAG_RD, 0, "Fan Information");
1035232482Sandreast			SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
1036273377Shselasky				       "minrpm", CTLFLAG_RD,
1037273377Shselasky				       &fan->fan.min_rpm, 0,
1038232482Sandreast				       "Minimum allowed RPM");
1039232482Sandreast			SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
1040273377Shselasky				       "maxrpm", CTLFLAG_RD,
1041273377Shselasky				       &fan->fan.max_rpm, 0,
1042232482Sandreast				       "Maximum allowed RPM");
1043232482Sandreast			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
1044232482Sandreast					"rpm",CTLTYPE_INT | CTLFLAG_RW |
1045232482Sandreast					CTLFLAG_MPSAFE, dev, i,
1046232482Sandreast					smu_fanrpm_sysctl, "I", "Fan RPM");
1047204082Snwhitehorn
1048232482Sandreast			fan->fan.read = (int (*)(struct pmac_fan *))smu_fan_read_rpm;
1049232482Sandreast			fan->fan.set = (int (*)(struct pmac_fan *, int))smu_fan_set_rpm;
1050204179Snwhitehorn
1051232482Sandreast		} else {
1052232482Sandreast			oid = SYSCTL_ADD_NODE(ctx,
1053232482Sandreast					      SYSCTL_CHILDREN(fanroot_oid),
1054232482Sandreast					      OID_AUTO, sysctl_name,
1055232482Sandreast					      CTLFLAG_RD, 0, "Fan Information");
1056232482Sandreast			SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
1057273377Shselasky				       "minpwm", CTLFLAG_RD,
1058273377Shselasky				       &fan->fan.min_rpm, 0,
1059232482Sandreast				       "Minimum allowed PWM in %");
1060232482Sandreast			SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
1061273377Shselasky				       "maxpwm", CTLFLAG_RD,
1062273377Shselasky				       &fan->fan.max_rpm, 0,
1063232482Sandreast				       "Maximum allowed PWM in %");
1064232482Sandreast			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
1065232482Sandreast					"pwm",CTLTYPE_INT | CTLFLAG_RW |
1066232482Sandreast					CTLFLAG_MPSAFE, dev,
1067232482Sandreast					SMU_PWM_SYSCTL_PWM | i,
1068232482Sandreast					smu_fanrpm_sysctl, "I", "Fan PWM in %");
1069232482Sandreast			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
1070232482Sandreast					"rpm",CTLTYPE_INT | CTLFLAG_RD |
1071232482Sandreast					CTLFLAG_MPSAFE, dev,
1072232482Sandreast					SMU_PWM_SYSCTL_RPM | i,
1073232482Sandreast					smu_fanrpm_sysctl, "I", "Fan RPM");
1074232482Sandreast			fan->fan.read = NULL;
1075232482Sandreast			fan->fan.set = (int (*)(struct pmac_fan *, int))smu_fan_set_pwm;
1076204179Snwhitehorn
1077204082Snwhitehorn		}
1078232482Sandreast		if (bootverbose)
1079232482Sandreast			device_printf(dev, "Fan: %s type: %d\n",
1080232482Sandreast				      fan->fan.name, fan->type);
1081204082Snwhitehorn	}
1082204082Snwhitehorn}
1083204082Snwhitehorn
1084204082Snwhitehornstatic int
1085222429Snwhitehornsmu_sensor_read(struct smu_sensor *sens)
1086204082Snwhitehorn{
1087222429Snwhitehorn	device_t smu = sens->dev;
1088204082Snwhitehorn	struct smu_cmd cmd;
1089204082Snwhitehorn	struct smu_softc *sc;
1090204082Snwhitehorn	int64_t value;
1091204692Snwhitehorn	int error;
1092204082Snwhitehorn
1093204082Snwhitehorn	cmd.cmd = SMU_ADC;
1094204082Snwhitehorn	cmd.len = 1;
1095204082Snwhitehorn	cmd.data[0] = sens->reg;
1096204692Snwhitehorn	error = 0;
1097204082Snwhitehorn
1098204692Snwhitehorn	error = smu_run_cmd(smu, &cmd, 1);
1099204692Snwhitehorn	if (error != 0)
1100222429Snwhitehorn		return (-1);
1101204082Snwhitehorn
1102204082Snwhitehorn	sc = device_get_softc(smu);
1103204082Snwhitehorn	value = (cmd.data[0] << 8) | cmd.data[1];
1104204082Snwhitehorn
1105204082Snwhitehorn	switch (sens->type) {
1106204082Snwhitehorn	case SMU_TEMP_SENSOR:
1107204082Snwhitehorn		value *= sc->sc_cpu_diode_scale;
1108204082Snwhitehorn		value >>= 3;
1109204082Snwhitehorn		value += ((int64_t)sc->sc_cpu_diode_offset) << 9;
1110204082Snwhitehorn		value <<= 1;
1111204082Snwhitehorn
1112222429Snwhitehorn		/* Convert from 16.16 fixed point degC into integer 0.1 K. */
1113300421Sloos		value = 10*(value >> 16) + ((10*(value & 0xffff)) >> 16) + 2731;
1114204082Snwhitehorn		break;
1115204082Snwhitehorn	case SMU_VOLTAGE_SENSOR:
1116204082Snwhitehorn		value *= sc->sc_cpu_volt_scale;
1117204082Snwhitehorn		value += sc->sc_cpu_volt_offset;
1118204082Snwhitehorn		value <<= 4;
1119204082Snwhitehorn
1120204082Snwhitehorn		/* Convert from 16.16 fixed point V into mV. */
1121204082Snwhitehorn		value *= 15625;
1122204082Snwhitehorn		value /= 1024;
1123204082Snwhitehorn		value /= 1000;
1124204082Snwhitehorn		break;
1125204082Snwhitehorn	case SMU_CURRENT_SENSOR:
1126204082Snwhitehorn		value *= sc->sc_cpu_curr_scale;
1127204082Snwhitehorn		value += sc->sc_cpu_curr_offset;
1128204082Snwhitehorn		value <<= 4;
1129204082Snwhitehorn
1130204082Snwhitehorn		/* Convert from 16.16 fixed point A into mA. */
1131204082Snwhitehorn		value *= 15625;
1132204082Snwhitehorn		value /= 1024;
1133204082Snwhitehorn		value /= 1000;
1134204082Snwhitehorn		break;
1135204082Snwhitehorn	case SMU_POWER_SENSOR:
1136204082Snwhitehorn		value *= sc->sc_slots_pow_scale;
1137204082Snwhitehorn		value += sc->sc_slots_pow_offset;
1138204082Snwhitehorn		value <<= 4;
1139204082Snwhitehorn
1140204082Snwhitehorn		/* Convert from 16.16 fixed point W into mW. */
1141204082Snwhitehorn		value *= 15625;
1142204082Snwhitehorn		value /= 1024;
1143204082Snwhitehorn		value /= 1000;
1144204082Snwhitehorn		break;
1145204082Snwhitehorn	}
1146204082Snwhitehorn
1147222429Snwhitehorn	return (value);
1148204082Snwhitehorn}
1149204082Snwhitehorn
1150204082Snwhitehornstatic int
1151204082Snwhitehornsmu_sensor_sysctl(SYSCTL_HANDLER_ARGS)
1152204082Snwhitehorn{
1153204082Snwhitehorn	device_t smu;
1154204082Snwhitehorn	struct smu_softc *sc;
1155204082Snwhitehorn	struct smu_sensor *sens;
1156204082Snwhitehorn	int value, error;
1157204082Snwhitehorn
1158204082Snwhitehorn	smu = arg1;
1159204082Snwhitehorn	sc = device_get_softc(smu);
1160204082Snwhitehorn	sens = &sc->sc_sensors[arg2];
1161204082Snwhitehorn
1162222429Snwhitehorn	value = smu_sensor_read(sens);
1163222429Snwhitehorn	if (value < 0)
1164222429Snwhitehorn		return (EBUSY);
1165204692Snwhitehorn
1166204082Snwhitehorn	error = sysctl_handle_int(oidp, &value, 0, req);
1167204082Snwhitehorn
1168204082Snwhitehorn	return (error);
1169204082Snwhitehorn}
1170204082Snwhitehorn
1171204082Snwhitehornstatic void
1172204082Snwhitehornsmu_attach_sensors(device_t dev, phandle_t sensroot)
1173204082Snwhitehorn{
1174204082Snwhitehorn	struct smu_sensor *sens;
1175204082Snwhitehorn	struct smu_softc *sc;
1176204082Snwhitehorn	struct sysctl_oid *sensroot_oid;
1177204082Snwhitehorn	struct sysctl_ctx_list *ctx;
1178204082Snwhitehorn	phandle_t child;
1179204082Snwhitehorn	char type[32];
1180204082Snwhitehorn	int i;
1181204082Snwhitehorn
1182204082Snwhitehorn	sc = device_get_softc(dev);
1183204082Snwhitehorn	sc->sc_nsensors = 0;
1184204082Snwhitehorn
1185204082Snwhitehorn	for (child = OF_child(sensroot); child != 0; child = OF_peer(child))
1186204082Snwhitehorn		sc->sc_nsensors++;
1187204082Snwhitehorn
1188204082Snwhitehorn	if (sc->sc_nsensors == 0) {
1189204082Snwhitehorn		device_printf(dev, "WARNING: No sensors detected!\n");
1190204082Snwhitehorn		return;
1191204082Snwhitehorn	}
1192204082Snwhitehorn
1193204179Snwhitehorn	sc->sc_sensors = malloc(sc->sc_nsensors * sizeof(struct smu_sensor),
1194204179Snwhitehorn	    M_SMU, M_WAITOK | M_ZERO);
1195204082Snwhitehorn
1196204082Snwhitehorn	sens = sc->sc_sensors;
1197204082Snwhitehorn	sc->sc_nsensors = 0;
1198204082Snwhitehorn
1199204082Snwhitehorn	ctx = device_get_sysctl_ctx(dev);
1200204082Snwhitehorn	sensroot_oid = SYSCTL_ADD_NODE(ctx,
1201204082Snwhitehorn	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensors",
1202204082Snwhitehorn	    CTLFLAG_RD, 0, "SMU Sensor Information");
1203204082Snwhitehorn
1204204082Snwhitehorn	for (child = OF_child(sensroot); child != 0; child = OF_peer(child)) {
1205204082Snwhitehorn		char sysctl_name[40], sysctl_desc[40];
1206204082Snwhitehorn		const char *units;
1207204082Snwhitehorn
1208222429Snwhitehorn		sens->dev = dev;
1209204082Snwhitehorn		OF_getprop(child, "device_type", type, sizeof(type));
1210204082Snwhitehorn
1211204082Snwhitehorn		if (strcmp(type, "current-sensor") == 0) {
1212204082Snwhitehorn			sens->type = SMU_CURRENT_SENSOR;
1213204082Snwhitehorn			units = "mA";
1214204082Snwhitehorn		} else if (strcmp(type, "temp-sensor") == 0) {
1215204082Snwhitehorn			sens->type = SMU_TEMP_SENSOR;
1216204082Snwhitehorn			units = "C";
1217204082Snwhitehorn		} else if (strcmp(type, "voltage-sensor") == 0) {
1218204082Snwhitehorn			sens->type = SMU_VOLTAGE_SENSOR;
1219204082Snwhitehorn			units = "mV";
1220204082Snwhitehorn		} else if (strcmp(type, "power-sensor") == 0) {
1221204082Snwhitehorn			sens->type = SMU_POWER_SENSOR;
1222204082Snwhitehorn			units = "mW";
1223204082Snwhitehorn		} else {
1224204082Snwhitehorn			continue;
1225204082Snwhitehorn		}
1226204082Snwhitehorn
1227204082Snwhitehorn		OF_getprop(child, "reg", &sens->reg, sizeof(cell_t));
1228222429Snwhitehorn		OF_getprop(child, "zone", &sens->therm.zone, sizeof(int));
1229222429Snwhitehorn		OF_getprop(child, "location", sens->therm.name,
1230222429Snwhitehorn		    sizeof(sens->therm.name));
1231204082Snwhitehorn
1232222429Snwhitehorn		for (i = 0; i < strlen(sens->therm.name); i++) {
1233222429Snwhitehorn			sysctl_name[i] = tolower(sens->therm.name[i]);
1234204082Snwhitehorn			if (isspace(sysctl_name[i]))
1235204082Snwhitehorn				sysctl_name[i] = '_';
1236204082Snwhitehorn		}
1237204082Snwhitehorn		sysctl_name[i] = 0;
1238204082Snwhitehorn
1239222429Snwhitehorn		sprintf(sysctl_desc,"%s (%s)", sens->therm.name, units);
1240204082Snwhitehorn
1241204082Snwhitehorn		SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sensroot_oid), OID_AUTO,
1242208841Snwhitehorn		    sysctl_name, CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
1243222429Snwhitehorn		    dev, sc->sc_nsensors, smu_sensor_sysctl,
1244222429Snwhitehorn		    (sens->type == SMU_TEMP_SENSOR) ? "IK" : "I", sysctl_desc);
1245204082Snwhitehorn
1246222429Snwhitehorn		if (sens->type == SMU_TEMP_SENSOR) {
1247222429Snwhitehorn			/* Make up some numbers */
1248300421Sloos			sens->therm.target_temp = 500 + 2731; /* 50 C */
1249300421Sloos			sens->therm.max_temp = 900 + 2731; /* 90 C */
1250222429Snwhitehorn
1251222429Snwhitehorn			sens->therm.read =
1252222429Snwhitehorn			    (int (*)(struct pmac_therm *))smu_sensor_read;
1253222429Snwhitehorn			pmac_thermal_sensor_register(&sens->therm);
1254222429Snwhitehorn		}
1255222429Snwhitehorn
1256204082Snwhitehorn		sens++;
1257204082Snwhitehorn		sc->sc_nsensors++;
1258204082Snwhitehorn	}
1259204082Snwhitehorn}
1260204082Snwhitehorn
1261204692Snwhitehornstatic void
1262204218Snwhitehornsmu_set_sleepled(void *xdev, int onoff)
1263204218Snwhitehorn{
1264204692Snwhitehorn	static struct smu_cmd cmd;
1265204218Snwhitehorn	device_t smu = xdev;
1266204218Snwhitehorn
1267204218Snwhitehorn	cmd.cmd = SMU_MISC;
1268204218Snwhitehorn	cmd.len = 3;
1269204218Snwhitehorn	cmd.data[0] = SMU_MISC_LED_CTRL;
1270204218Snwhitehorn	cmd.data[1] = 0;
1271204218Snwhitehorn	cmd.data[2] = onoff;
1272204218Snwhitehorn
1273204692Snwhitehorn	smu_run_cmd(smu, &cmd, 0);
1274204218Snwhitehorn}
1275204218Snwhitehorn
1276204270Snwhitehornstatic int
1277204270Snwhitehornsmu_server_mode(SYSCTL_HANDLER_ARGS)
1278204270Snwhitehorn{
1279204270Snwhitehorn	struct smu_cmd cmd;
1280204270Snwhitehorn	u_int server_mode;
1281204270Snwhitehorn	device_t smu = arg1;
1282204270Snwhitehorn	int error;
1283204270Snwhitehorn
1284204270Snwhitehorn	cmd.cmd = SMU_POWER_EVENTS;
1285204270Snwhitehorn	cmd.len = 1;
1286204270Snwhitehorn	cmd.data[0] = SMU_PWR_GET_POWERUP;
1287204270Snwhitehorn
1288204692Snwhitehorn	error = smu_run_cmd(smu, &cmd, 1);
1289204270Snwhitehorn
1290204270Snwhitehorn	if (error)
1291204270Snwhitehorn		return (error);
1292204270Snwhitehorn
1293204270Snwhitehorn	server_mode = (cmd.data[1] & SMU_WAKEUP_AC_INSERT) ? 1 : 0;
1294204270Snwhitehorn
1295204270Snwhitehorn	error = sysctl_handle_int(oidp, &server_mode, 0, req);
1296204270Snwhitehorn
1297204270Snwhitehorn	if (error || !req->newptr)
1298204270Snwhitehorn		return (error);
1299204270Snwhitehorn
1300204270Snwhitehorn	if (server_mode == 1)
1301204270Snwhitehorn		cmd.data[0] = SMU_PWR_SET_POWERUP;
1302204270Snwhitehorn	else if (server_mode == 0)
1303204270Snwhitehorn		cmd.data[0] = SMU_PWR_CLR_POWERUP;
1304204270Snwhitehorn	else
1305204270Snwhitehorn		return (EINVAL);
1306204270Snwhitehorn
1307204270Snwhitehorn	cmd.len = 3;
1308204270Snwhitehorn	cmd.data[1] = 0;
1309204270Snwhitehorn	cmd.data[2] = SMU_WAKEUP_AC_INSERT;
1310204270Snwhitehorn
1311204692Snwhitehorn	return (smu_run_cmd(smu, &cmd, 1));
1312204270Snwhitehorn}
1313204270Snwhitehorn
1314212054Snwhitehornstatic void
1315212054Snwhitehornsmu_shutdown(void *xdev, int howto)
1316212054Snwhitehorn{
1317212054Snwhitehorn	device_t smu = xdev;
1318212054Snwhitehorn	struct smu_cmd cmd;
1319212054Snwhitehorn
1320212054Snwhitehorn	cmd.cmd = SMU_POWER;
1321212054Snwhitehorn	if (howto & RB_HALT)
1322212054Snwhitehorn		strcpy(cmd.data, "SHUTDOWN");
1323212054Snwhitehorn	else
1324212054Snwhitehorn		strcpy(cmd.data, "RESTART");
1325212054Snwhitehorn
1326212054Snwhitehorn	cmd.len = strlen(cmd.data);
1327212054Snwhitehorn
1328212054Snwhitehorn	smu_run_cmd(smu, &cmd, 1);
1329212054Snwhitehorn
1330212054Snwhitehorn	for (;;);
1331212054Snwhitehorn}
1332212054Snwhitehorn
1333205506Snwhitehornstatic int
1334205506Snwhitehornsmu_gettime(device_t dev, struct timespec *ts)
1335205506Snwhitehorn{
1336205506Snwhitehorn	struct smu_cmd cmd;
1337205506Snwhitehorn	struct clocktime ct;
1338205506Snwhitehorn
1339205506Snwhitehorn	cmd.cmd = SMU_RTC;
1340205506Snwhitehorn	cmd.len = 1;
1341205506Snwhitehorn	cmd.data[0] = SMU_RTC_GET;
1342205506Snwhitehorn
1343205506Snwhitehorn	if (smu_run_cmd(dev, &cmd, 1) != 0)
1344205506Snwhitehorn		return (ENXIO);
1345205506Snwhitehorn
1346205506Snwhitehorn	ct.nsec	= 0;
1347205506Snwhitehorn	ct.sec	= bcd2bin(cmd.data[0]);
1348205506Snwhitehorn	ct.min	= bcd2bin(cmd.data[1]);
1349205506Snwhitehorn	ct.hour	= bcd2bin(cmd.data[2]);
1350205506Snwhitehorn	ct.dow	= bcd2bin(cmd.data[3]);
1351205506Snwhitehorn	ct.day	= bcd2bin(cmd.data[4]);
1352205506Snwhitehorn	ct.mon	= bcd2bin(cmd.data[5]);
1353205506Snwhitehorn	ct.year	= bcd2bin(cmd.data[6]) + 2000;
1354205506Snwhitehorn
1355205506Snwhitehorn	return (clock_ct_to_ts(&ct, ts));
1356205506Snwhitehorn}
1357205506Snwhitehorn
1358205506Snwhitehornstatic int
1359205506Snwhitehornsmu_settime(device_t dev, struct timespec *ts)
1360205506Snwhitehorn{
1361219624Snwhitehorn	static struct smu_cmd cmd;
1362205506Snwhitehorn	struct clocktime ct;
1363205506Snwhitehorn
1364205506Snwhitehorn	cmd.cmd = SMU_RTC;
1365205506Snwhitehorn	cmd.len = 8;
1366205506Snwhitehorn	cmd.data[0] = SMU_RTC_SET;
1367205506Snwhitehorn
1368205506Snwhitehorn	clock_ts_to_ct(ts, &ct);
1369205506Snwhitehorn
1370205506Snwhitehorn	cmd.data[1] = bin2bcd(ct.sec);
1371205506Snwhitehorn	cmd.data[2] = bin2bcd(ct.min);
1372205506Snwhitehorn	cmd.data[3] = bin2bcd(ct.hour);
1373205506Snwhitehorn	cmd.data[4] = bin2bcd(ct.dow);
1374205506Snwhitehorn	cmd.data[5] = bin2bcd(ct.day);
1375205506Snwhitehorn	cmd.data[6] = bin2bcd(ct.mon);
1376205506Snwhitehorn	cmd.data[7] = bin2bcd(ct.year - 2000);
1377205506Snwhitehorn
1378219624Snwhitehorn	return (smu_run_cmd(dev, &cmd, 0));
1379205506Snwhitehorn}
1380205506Snwhitehorn
1381208841Snwhitehorn/* SMU I2C Interface */
1382208841Snwhitehorn
1383208841Snwhitehornstatic int smuiic_probe(device_t dev);
1384208841Snwhitehornstatic int smuiic_attach(device_t dev);
1385208841Snwhitehornstatic int smuiic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs);
1386208841Snwhitehornstatic phandle_t smuiic_get_node(device_t bus, device_t dev);
1387208841Snwhitehorn
1388208841Snwhitehornstatic device_method_t smuiic_methods[] = {
1389208841Snwhitehorn	/* device interface */
1390208841Snwhitehorn	DEVMETHOD(device_probe,         smuiic_probe),
1391208841Snwhitehorn	DEVMETHOD(device_attach,        smuiic_attach),
1392208841Snwhitehorn
1393208841Snwhitehorn	/* iicbus interface */
1394208841Snwhitehorn	DEVMETHOD(iicbus_callback,      iicbus_null_callback),
1395208841Snwhitehorn	DEVMETHOD(iicbus_transfer,      smuiic_transfer),
1396208841Snwhitehorn
1397208841Snwhitehorn	/* ofw_bus interface */
1398208841Snwhitehorn	DEVMETHOD(ofw_bus_get_node,     smuiic_get_node),
1399208841Snwhitehorn
1400208841Snwhitehorn	{ 0, 0 }
1401208841Snwhitehorn};
1402208841Snwhitehorn
1403208841Snwhitehornstruct smuiic_softc {
1404208841Snwhitehorn	struct mtx	sc_mtx;
1405208841Snwhitehorn	volatile int	sc_iic_inuse;
1406208841Snwhitehorn	int		sc_busno;
1407208841Snwhitehorn};
1408208841Snwhitehorn
1409208841Snwhitehornstatic driver_t smuiic_driver = {
1410208841Snwhitehorn	"iichb",
1411208841Snwhitehorn	smuiic_methods,
1412208841Snwhitehorn	sizeof(struct smuiic_softc)
1413208841Snwhitehorn};
1414208841Snwhitehornstatic devclass_t smuiic_devclass;
1415208841Snwhitehorn
1416208841SnwhitehornDRIVER_MODULE(smuiic, smu, smuiic_driver, smuiic_devclass, 0, 0);
1417208841Snwhitehorn
1418208841Snwhitehornstatic void
1419208841Snwhitehornsmu_attach_i2c(device_t smu, phandle_t i2croot)
1420208841Snwhitehorn{
1421208841Snwhitehorn	phandle_t child;
1422208841Snwhitehorn	device_t cdev;
1423208841Snwhitehorn	struct ofw_bus_devinfo *dinfo;
1424208841Snwhitehorn	char name[32];
1425208841Snwhitehorn
1426208841Snwhitehorn	for (child = OF_child(i2croot); child != 0; child = OF_peer(child)) {
1427208841Snwhitehorn		if (OF_getprop(child, "name", name, sizeof(name)) <= 0)
1428208841Snwhitehorn			continue;
1429208841Snwhitehorn
1430208841Snwhitehorn		if (strcmp(name, "i2c-bus") != 0 && strcmp(name, "i2c") != 0)
1431208841Snwhitehorn			continue;
1432208841Snwhitehorn
1433208841Snwhitehorn		dinfo = malloc(sizeof(struct ofw_bus_devinfo), M_SMU,
1434208841Snwhitehorn		    M_WAITOK | M_ZERO);
1435208841Snwhitehorn		if (ofw_bus_gen_setup_devinfo(dinfo, child) != 0) {
1436208841Snwhitehorn			free(dinfo, M_SMU);
1437208841Snwhitehorn			continue;
1438208841Snwhitehorn		}
1439208841Snwhitehorn
1440208841Snwhitehorn		cdev = device_add_child(smu, NULL, -1);
1441208841Snwhitehorn		if (cdev == NULL) {
1442208841Snwhitehorn			device_printf(smu, "<%s>: device_add_child failed\n",
1443208841Snwhitehorn			    dinfo->obd_name);
1444208841Snwhitehorn			ofw_bus_gen_destroy_devinfo(dinfo);
1445208841Snwhitehorn			free(dinfo, M_SMU);
1446208841Snwhitehorn			continue;
1447208841Snwhitehorn		}
1448208841Snwhitehorn		device_set_ivars(cdev, dinfo);
1449208841Snwhitehorn	}
1450208841Snwhitehorn}
1451208841Snwhitehorn
1452208841Snwhitehornstatic int
1453208841Snwhitehornsmuiic_probe(device_t dev)
1454208841Snwhitehorn{
1455208841Snwhitehorn	const char *name;
1456208841Snwhitehorn
1457208841Snwhitehorn	name = ofw_bus_get_name(dev);
1458208841Snwhitehorn	if (name == NULL)
1459208841Snwhitehorn		return (ENXIO);
1460208841Snwhitehorn
1461208841Snwhitehorn	if (strcmp(name, "i2c-bus") == 0 || strcmp(name, "i2c") == 0) {
1462208841Snwhitehorn		device_set_desc(dev, "SMU I2C controller");
1463208841Snwhitehorn		return (0);
1464208841Snwhitehorn	}
1465208841Snwhitehorn
1466208841Snwhitehorn	return (ENXIO);
1467208841Snwhitehorn}
1468208841Snwhitehorn
1469208841Snwhitehornstatic int
1470208841Snwhitehornsmuiic_attach(device_t dev)
1471208841Snwhitehorn{
1472208841Snwhitehorn	struct smuiic_softc *sc = device_get_softc(dev);
1473208841Snwhitehorn	mtx_init(&sc->sc_mtx, "smuiic", NULL, MTX_DEF);
1474208841Snwhitehorn	sc->sc_iic_inuse = 0;
1475208841Snwhitehorn
1476208841Snwhitehorn	/* Get our bus number */
1477208841Snwhitehorn	OF_getprop(ofw_bus_get_node(dev), "reg", &sc->sc_busno,
1478208841Snwhitehorn	    sizeof(sc->sc_busno));
1479208841Snwhitehorn
1480208841Snwhitehorn	/* Add the IIC bus layer */
1481208841Snwhitehorn	device_add_child(dev, "iicbus", -1);
1482208841Snwhitehorn
1483208841Snwhitehorn	return (bus_generic_attach(dev));
1484208841Snwhitehorn}
1485208841Snwhitehorn
1486208841Snwhitehornstatic int
1487208841Snwhitehornsmuiic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
1488208841Snwhitehorn{
1489208841Snwhitehorn	struct smuiic_softc *sc = device_get_softc(dev);
1490208841Snwhitehorn	struct smu_cmd cmd;
1491208841Snwhitehorn	int i, j, error;
1492208841Snwhitehorn
1493208841Snwhitehorn	mtx_lock(&sc->sc_mtx);
1494208841Snwhitehorn	while (sc->sc_iic_inuse)
1495208841Snwhitehorn		mtx_sleep(sc, &sc->sc_mtx, 0, "smuiic", 100);
1496208841Snwhitehorn
1497208841Snwhitehorn	sc->sc_iic_inuse = 1;
1498208841Snwhitehorn	error = 0;
1499208841Snwhitehorn
1500208841Snwhitehorn	for (i = 0; i < nmsgs; i++) {
1501208841Snwhitehorn		cmd.cmd = SMU_I2C;
1502208841Snwhitehorn		cmd.data[0] = sc->sc_busno;
1503208841Snwhitehorn		if (msgs[i].flags & IIC_M_NOSTOP)
1504208841Snwhitehorn			cmd.data[1] = SMU_I2C_COMBINED;
1505208841Snwhitehorn		else
1506208841Snwhitehorn			cmd.data[1] = SMU_I2C_SIMPLE;
1507208841Snwhitehorn
1508208841Snwhitehorn		cmd.data[2] = msgs[i].slave;
1509208841Snwhitehorn		if (msgs[i].flags & IIC_M_RD)
1510208841Snwhitehorn			cmd.data[2] |= 1;
1511208841Snwhitehorn
1512208841Snwhitehorn		if (msgs[i].flags & IIC_M_NOSTOP) {
1513208841Snwhitehorn			KASSERT(msgs[i].len < 4,
1514208841Snwhitehorn			    ("oversize I2C combined message"));
1515208841Snwhitehorn
1516208841Snwhitehorn			cmd.data[3] = min(msgs[i].len, 3);
1517208841Snwhitehorn			memcpy(&cmd.data[4], msgs[i].buf, min(msgs[i].len, 3));
1518208841Snwhitehorn			i++; /* Advance to next part of message */
1519208841Snwhitehorn		} else {
1520208841Snwhitehorn			cmd.data[3] = 0;
1521208841Snwhitehorn			memset(&cmd.data[4], 0, 3);
1522208841Snwhitehorn		}
1523208841Snwhitehorn
1524208841Snwhitehorn		cmd.data[7] = msgs[i].slave;
1525208841Snwhitehorn		if (msgs[i].flags & IIC_M_RD)
1526208841Snwhitehorn			cmd.data[7] |= 1;
1527208841Snwhitehorn
1528208841Snwhitehorn		cmd.data[8] = msgs[i].len;
1529208841Snwhitehorn		if (msgs[i].flags & IIC_M_RD) {
1530208841Snwhitehorn			memset(&cmd.data[9], 0xff, msgs[i].len);
1531208841Snwhitehorn			cmd.len = 9;
1532208841Snwhitehorn		} else {
1533208841Snwhitehorn			memcpy(&cmd.data[9], msgs[i].buf, msgs[i].len);
1534208841Snwhitehorn			cmd.len = 9 + msgs[i].len;
1535208841Snwhitehorn		}
1536208841Snwhitehorn
1537208841Snwhitehorn		mtx_unlock(&sc->sc_mtx);
1538208841Snwhitehorn		smu_run_cmd(device_get_parent(dev), &cmd, 1);
1539208841Snwhitehorn		mtx_lock(&sc->sc_mtx);
1540208841Snwhitehorn
1541208841Snwhitehorn		for (j = 0; j < 10; j++) {
1542208841Snwhitehorn			cmd.cmd = SMU_I2C;
1543208841Snwhitehorn			cmd.len = 1;
1544208841Snwhitehorn			cmd.data[0] = 0;
1545208841Snwhitehorn			memset(&cmd.data[1], 0xff, msgs[i].len);
1546208841Snwhitehorn
1547208841Snwhitehorn			mtx_unlock(&sc->sc_mtx);
1548208841Snwhitehorn			smu_run_cmd(device_get_parent(dev), &cmd, 1);
1549208841Snwhitehorn			mtx_lock(&sc->sc_mtx);
1550208841Snwhitehorn
1551208841Snwhitehorn			if (!(cmd.data[0] & 0x80))
1552208841Snwhitehorn				break;
1553208841Snwhitehorn
1554208841Snwhitehorn			mtx_sleep(sc, &sc->sc_mtx, 0, "smuiic", 10);
1555208841Snwhitehorn		}
1556208841Snwhitehorn
1557208841Snwhitehorn		if (cmd.data[0] & 0x80) {
1558208841Snwhitehorn			error = EIO;
1559208841Snwhitehorn			msgs[i].len = 0;
1560208841Snwhitehorn			goto exit;
1561208841Snwhitehorn		}
1562208841Snwhitehorn		memcpy(msgs[i].buf, &cmd.data[1], msgs[i].len);
1563208841Snwhitehorn		msgs[i].len = cmd.len - 1;
1564208841Snwhitehorn	}
1565208841Snwhitehorn
1566208841Snwhitehorn    exit:
1567208841Snwhitehorn	sc->sc_iic_inuse = 0;
1568208841Snwhitehorn	mtx_unlock(&sc->sc_mtx);
1569208841Snwhitehorn	wakeup(sc);
1570208841Snwhitehorn	return (error);
1571208841Snwhitehorn}
1572208841Snwhitehorn
1573208841Snwhitehornstatic phandle_t
1574208841Snwhitehornsmuiic_get_node(device_t bus, device_t dev)
1575208841Snwhitehorn{
1576208841Snwhitehorn
1577208841Snwhitehorn	return (ofw_bus_get_node(bus));
1578208841Snwhitehorn}
1579208841Snwhitehorn
1580