1261421Sjhibbits/*-
2261421Sjhibbits * Copyright (c) 2012 Andreas Tobler
3261421Sjhibbits * Copyright (c) 2014 Justin Hibbits
4261421Sjhibbits * All rights reserved.
5261421Sjhibbits *
6261421Sjhibbits * Redistribution and use in source and binary forms, with or without
7261421Sjhibbits * modification, are permitted provided that the following conditions
8261421Sjhibbits * are met:
9261421Sjhibbits * 1. Redistributions of source code must retain the above copyright
10261421Sjhibbits *    notice, this list of conditions and the following disclaimer.
11261421Sjhibbits * 2. Redistributions in binary form must reproduce the above copyright
12261421Sjhibbits *    notice, this list of conditions and the following disclaimer in the
13261421Sjhibbits *    documentation and/or other materials provided with the distribution.
14261421Sjhibbits *
15261421Sjhibbits * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16261421Sjhibbits * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17261421Sjhibbits * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18261421Sjhibbits * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19261421Sjhibbits * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20261421Sjhibbits * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21261421Sjhibbits * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22261421Sjhibbits * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23261421Sjhibbits * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24261421Sjhibbits * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25261421Sjhibbits * SUCH DAMAGE.
26261421Sjhibbits */
27261421Sjhibbits
28261421Sjhibbits#include <sys/cdefs.h>
29261421Sjhibbits__FBSDID("$FreeBSD$");
30261421Sjhibbits
31261421Sjhibbits#include <sys/param.h>
32261421Sjhibbits#include <sys/bus.h>
33261421Sjhibbits#include <sys/systm.h>
34261421Sjhibbits#include <sys/module.h>
35261421Sjhibbits#include <sys/callout.h>
36261421Sjhibbits#include <sys/conf.h>
37261421Sjhibbits#include <sys/cpu.h>
38261421Sjhibbits#include <sys/ctype.h>
39261421Sjhibbits#include <sys/kernel.h>
40261421Sjhibbits#include <sys/reboot.h>
41261421Sjhibbits#include <sys/rman.h>
42261421Sjhibbits#include <sys/sysctl.h>
43261421Sjhibbits#include <sys/limits.h>
44261421Sjhibbits
45261421Sjhibbits#include <machine/bus.h>
46261421Sjhibbits#include <machine/md_var.h>
47261421Sjhibbits
48261421Sjhibbits#include <dev/iicbus/iicbus.h>
49261421Sjhibbits#include <dev/iicbus/iiconf.h>
50261421Sjhibbits
51261421Sjhibbits#include <dev/ofw/openfirm.h>
52261421Sjhibbits#include <dev/ofw/ofw_bus.h>
53261421Sjhibbits#include <powerpc/powermac/powermac_thermal.h>
54261421Sjhibbits
55261421Sjhibbits/* ADT746X registers. */
56261421Sjhibbits#define ADT746X_TACH1LOW          0x28
57261421Sjhibbits#define ADT746X_TACH1HIGH         0x29
58261421Sjhibbits#define ADT746X_TACH2LOW          0x2a
59261421Sjhibbits#define ADT746X_TACH2HIGH         0x2b
60261421Sjhibbits#define ADT746X_PWM1              0x30
61261421Sjhibbits#define ADT746X_PWM2              0x31
62261421Sjhibbits#define ADT746X_DEVICE_ID         0x3d
63261421Sjhibbits#define ADT746X_COMPANY_ID        0x3e
64261421Sjhibbits#define ADT746X_REV_ID            0x3f
65261421Sjhibbits#define ADT746X_CONFIG            0x40
66261421Sjhibbits#define ADT746X_PWM1_CONF         0x5c
67261421Sjhibbits#define ADT746X_PWM2_CONF         0x5d
68261421Sjhibbits#define ADT746X_MANUAL_MASK       0xe0
69261421Sjhibbits
70261421Sjhibbits#define ADT7460_DEV_ID            0x27
71261421Sjhibbits#define ADT7467_DEV_ID            0x68
72261421Sjhibbits
73261421Sjhibbitsstruct adt746x_fan {
74261421Sjhibbits	struct pmac_fan fan;
75261421Sjhibbits	device_t        dev;
76261421Sjhibbits	int             id;
77261421Sjhibbits	int             setpoint;
78261421Sjhibbits	int		pwm_reg;
79261421Sjhibbits	int		conf_reg;
80261421Sjhibbits};
81261421Sjhibbits
82261421Sjhibbitsstruct adt746x_sensor {
83261421Sjhibbits	struct pmac_therm therm;
84261421Sjhibbits	device_t          dev;
85261421Sjhibbits	int               id;
86261421Sjhibbits	cell_t	          reg;
87261421Sjhibbits	enum {
88261421Sjhibbits		ADT746X_SENSOR_TEMP,
89261421Sjhibbits		ADT746X_SENSOR_VOLT,
90261421Sjhibbits		ADT746X_SENSOR_SPEED
91261421Sjhibbits	} type;
92261421Sjhibbits};
93261421Sjhibbits
94261421Sjhibbitsstruct adt746x_softc {
95261421Sjhibbits	device_t		sc_dev;
96261421Sjhibbits	struct intr_config_hook enum_hook;
97261421Sjhibbits	uint32_t                sc_addr;
98261421Sjhibbits	/* The 7467 supports up to 4 fans, 2 voltage and 3 temperature sensors. */
99261421Sjhibbits	struct adt746x_fan	sc_fans[4];
100261421Sjhibbits	int			sc_nfans;
101261421Sjhibbits	struct adt746x_sensor   sc_sensors[9];
102261421Sjhibbits	int			sc_nsensors;
103261421Sjhibbits	int                     device_id;
104261421Sjhibbits
105261421Sjhibbits};
106261421Sjhibbits
107261421Sjhibbits
108261421Sjhibbits/* Regular bus attachment functions */
109261421Sjhibbits
110261421Sjhibbitsstatic int  adt746x_probe(device_t);
111261421Sjhibbitsstatic int  adt746x_attach(device_t);
112261421Sjhibbits
113261421Sjhibbits
114261421Sjhibbits/* Utility functions */
115261421Sjhibbitsstatic void adt746x_attach_fans(device_t dev);
116261421Sjhibbitsstatic void adt746x_attach_sensors(device_t dev);
117261421Sjhibbitsstatic int  adt746x_fill_fan_prop(device_t dev);
118261421Sjhibbitsstatic int  adt746x_fill_sensor_prop(device_t dev);
119261421Sjhibbits
120261421Sjhibbitsstatic int  adt746x_fan_set_pwm(struct adt746x_fan *fan, int pwm);
121261421Sjhibbitsstatic int  adt746x_fan_get_pwm(struct adt746x_fan *fan);
122261421Sjhibbitsstatic int  adt746x_sensor_read(struct adt746x_sensor *sens);
123261421Sjhibbitsstatic void adt746x_start(void *xdev);
124261421Sjhibbits
125261421Sjhibbits/* i2c read/write functions. */
126261421Sjhibbitsstatic int  adt746x_write(device_t dev, uint32_t addr, uint8_t reg,
127261421Sjhibbits			  uint8_t *buf);
128261421Sjhibbitsstatic int  adt746x_read(device_t dev, uint32_t addr, uint8_t reg,
129261421Sjhibbits			 uint8_t *data);
130261421Sjhibbits
131261421Sjhibbitsstatic device_method_t  adt746x_methods[] = {
132261421Sjhibbits	/* Device interface */
133261421Sjhibbits	DEVMETHOD(device_probe,	 adt746x_probe),
134261421Sjhibbits	DEVMETHOD(device_attach, adt746x_attach),
135261421Sjhibbits	{ 0, 0 },
136261421Sjhibbits};
137261421Sjhibbits
138261421Sjhibbitsstatic driver_t adt746x_driver = {
139261421Sjhibbits	"adt746x",
140261421Sjhibbits	adt746x_methods,
141261421Sjhibbits	sizeof(struct adt746x_softc)
142261421Sjhibbits};
143261421Sjhibbits
144261421Sjhibbitsstatic devclass_t adt746x_devclass;
145261421Sjhibbits
146261421SjhibbitsDRIVER_MODULE(adt746x, iicbus, adt746x_driver, adt746x_devclass, 0, 0);
147261421Sjhibbitsstatic MALLOC_DEFINE(M_ADT746X, "adt746x", "ADT Sensor Information");
148261421Sjhibbits
149261421Sjhibbits
150261421Sjhibbits/* i2c read/write functions. */
151261421Sjhibbits
152261421Sjhibbitsstatic int
153261421Sjhibbitsadt746x_write(device_t dev, uint32_t addr, uint8_t reg, uint8_t *buff)
154261421Sjhibbits{
155261421Sjhibbits	uint8_t buf[4];
156261421Sjhibbits	int try = 0;
157261421Sjhibbits
158261421Sjhibbits	struct iic_msg msg[] = {
159261421Sjhibbits		{addr, IIC_M_WR, 2, buf }
160261421Sjhibbits	};
161261421Sjhibbits
162261421Sjhibbits	/* Prepare the write msg. */
163261421Sjhibbits	buf[0] = reg;
164261421Sjhibbits	memcpy(buf + 1, buff, 1);
165261421Sjhibbits
166261421Sjhibbits	for (;;)
167261421Sjhibbits	{
168261421Sjhibbits		if (iicbus_transfer(dev, msg, 1) == 0)
169261421Sjhibbits			return (0);
170261421Sjhibbits		if (++try > 5) {
171261421Sjhibbits			device_printf(dev, "iicbus write failed\n");
172261421Sjhibbits			return (-1);
173261421Sjhibbits		}
174261421Sjhibbits		pause("adt746x_write", hz);
175261421Sjhibbits	}
176261421Sjhibbits	return (0);
177261421Sjhibbits}
178261421Sjhibbits
179261421Sjhibbitsstatic int
180261421Sjhibbitsadt746x_read(device_t dev, uint32_t addr, uint8_t reg, uint8_t *data)
181261421Sjhibbits{
182261421Sjhibbits	uint8_t buf[4];
183261421Sjhibbits	int err, try = 0;
184261421Sjhibbits
185261421Sjhibbits	struct iic_msg msg[2] = {
186261421Sjhibbits		{addr, IIC_M_WR | IIC_M_NOSTOP, 1, &reg},
187261421Sjhibbits		{addr, IIC_M_RD, 1, buf},
188261421Sjhibbits	};
189261421Sjhibbits
190261421Sjhibbits	for (;;)
191261421Sjhibbits	{
192261421Sjhibbits		err = iicbus_transfer(dev, msg, 2);
193261421Sjhibbits		if (err != 0)
194261421Sjhibbits			goto retry;
195261421Sjhibbits
196261421Sjhibbits		*data = *((uint8_t*)buf);
197261421Sjhibbits		return (0);
198261421Sjhibbits	retry:
199261421Sjhibbits		if (++try > 5) {
200261421Sjhibbits			device_printf(dev, "iicbus read failed\n");
201261421Sjhibbits			return (-1);
202261421Sjhibbits		}
203261421Sjhibbits		pause("adt746x_read", hz);
204261421Sjhibbits	}
205261421Sjhibbits}
206261421Sjhibbits
207261421Sjhibbitsstatic int
208261421Sjhibbitsadt746x_probe(device_t dev)
209261421Sjhibbits{
210261421Sjhibbits	const char  *name, *compatible;
211261421Sjhibbits	struct adt746x_softc *sc;
212261421Sjhibbits
213261421Sjhibbits	name = ofw_bus_get_name(dev);
214261421Sjhibbits	compatible = ofw_bus_get_compat(dev);
215261421Sjhibbits
216261421Sjhibbits	if (!name)
217261421Sjhibbits		return (ENXIO);
218261421Sjhibbits
219261421Sjhibbits	if (strcmp(name, "fan") != 0 ||
220261421Sjhibbits	    (strcmp(compatible, "adt7460") != 0 &&
221261421Sjhibbits	     strcmp(compatible, "adt7467") != 0))
222261421Sjhibbits		return (ENXIO);
223261421Sjhibbits
224261421Sjhibbits	sc = device_get_softc(dev);
225261421Sjhibbits	sc->sc_dev = dev;
226261421Sjhibbits	sc->sc_addr = iicbus_get_addr(dev);
227261421Sjhibbits
228261421Sjhibbits	device_set_desc(dev, "Apple Thermostat Unit ADT746X");
229261421Sjhibbits
230261421Sjhibbits	return (0);
231261421Sjhibbits}
232261421Sjhibbits
233261421Sjhibbitsstatic int
234261421Sjhibbitsadt746x_attach(device_t dev)
235261421Sjhibbits{
236261421Sjhibbits	struct adt746x_softc *sc;
237261421Sjhibbits
238261421Sjhibbits	sc = device_get_softc(dev);
239261421Sjhibbits
240261421Sjhibbits	sc->enum_hook.ich_func = adt746x_start;
241261421Sjhibbits	sc->enum_hook.ich_arg = dev;
242261421Sjhibbits
243261421Sjhibbits	/* We have to wait until interrupts are enabled. I2C read and write
244261421Sjhibbits	 * only works if the interrupts are available.
245261421Sjhibbits	 * The unin/i2c is controlled by the htpic on unin. But this is not
246261421Sjhibbits	 * the master. The openpic on mac-io is controlling the htpic.
247261421Sjhibbits	 * This one gets attached after the mac-io probing and then the
248261421Sjhibbits	 * interrupts will be available.
249261421Sjhibbits	 */
250261421Sjhibbits
251261421Sjhibbits	if (config_intrhook_establish(&sc->enum_hook) != 0)
252261421Sjhibbits		return (ENOMEM);
253261421Sjhibbits
254261421Sjhibbits	return (0);
255261421Sjhibbits}
256261421Sjhibbits
257261421Sjhibbitsstatic void
258261421Sjhibbitsadt746x_start(void *xdev)
259261421Sjhibbits{
260261421Sjhibbits	uint8_t did, cid, rev, conf;
261261421Sjhibbits
262261421Sjhibbits	struct adt746x_softc *sc;
263261421Sjhibbits
264261421Sjhibbits	device_t dev = (device_t)xdev;
265261421Sjhibbits
266261421Sjhibbits	sc = device_get_softc(dev);
267261421Sjhibbits
268261421Sjhibbits	adt746x_read(sc->sc_dev, sc->sc_addr, ADT746X_DEVICE_ID, &did);
269261421Sjhibbits	adt746x_read(sc->sc_dev, sc->sc_addr, ADT746X_COMPANY_ID, &cid);
270261421Sjhibbits	adt746x_read(sc->sc_dev, sc->sc_addr, ADT746X_REV_ID, &rev);
271261421Sjhibbits	adt746x_read(sc->sc_dev, sc->sc_addr, ADT746X_CONFIG, &conf);
272261421Sjhibbits
273261421Sjhibbits	device_printf(dev, "Dev ID %#x, Company ID %#x, Rev ID %#x CNF: %#x\n",
274261421Sjhibbits		      did, cid, rev, conf);
275261421Sjhibbits
276261421Sjhibbits	/* We can get the device id either from 'of' properties or from the chip
277261421Sjhibbits	   itself. This method makes sure we can read the chip, otherwise
278261421Sjhibbits	   we return.  */
279261421Sjhibbits
280261421Sjhibbits	sc->device_id = did;
281261421Sjhibbits
282261421Sjhibbits	conf = 1;
283261421Sjhibbits	/* Start the ADT7460.  */
284261421Sjhibbits	if (sc->device_id == ADT7460_DEV_ID)
285261421Sjhibbits		adt746x_write(sc->sc_dev, sc->sc_addr, ADT746X_CONFIG, &conf);
286261421Sjhibbits
287261421Sjhibbits	/* Detect and attach child devices.  */
288261421Sjhibbits	adt746x_attach_fans(dev);
289261421Sjhibbits	adt746x_attach_sensors(dev);
290261421Sjhibbits	config_intrhook_disestablish(&sc->enum_hook);
291261421Sjhibbits}
292261421Sjhibbits
293261421Sjhibbits/*
294261421Sjhibbits * Sensor and fan management
295261421Sjhibbits */
296261421Sjhibbitsstatic int
297261421Sjhibbitsadt746x_fan_set_pwm(struct adt746x_fan *fan, int pwm)
298261421Sjhibbits{
299261421Sjhibbits	uint8_t reg = 0, manual, mode = 0;
300261421Sjhibbits	struct adt746x_softc *sc;
301261421Sjhibbits	uint8_t buf;
302261421Sjhibbits
303261421Sjhibbits	sc = device_get_softc(fan->dev);
304261421Sjhibbits
305261421Sjhibbits	/* Clamp to allowed range */
306261421Sjhibbits	pwm = max(fan->fan.min_rpm, pwm);
307261421Sjhibbits	pwm = min(fan->fan.max_rpm, pwm);
308261421Sjhibbits
309261421Sjhibbits	reg = fan->pwm_reg;
310261421Sjhibbits	mode = fan->conf_reg;
311261421Sjhibbits
312261421Sjhibbits	/* From the 7460 datasheet:
313261421Sjhibbits	   PWM dutycycle can be programmed from 0% (0x00) to 100% (0xFF)
314261421Sjhibbits	   in steps of 0.39% (256 steps).
315261421Sjhibbits	 */
316261421Sjhibbits	buf = (pwm * 100 / 39) - (pwm ? 1 : 0);
317261421Sjhibbits	fan->setpoint = buf;
318261421Sjhibbits
319261421Sjhibbits	/* Manual mode.  */
320261421Sjhibbits	adt746x_read(sc->sc_dev, sc->sc_addr, mode, &manual);
321261421Sjhibbits	manual |= ADT746X_MANUAL_MASK;
322261421Sjhibbits	adt746x_write(sc->sc_dev, sc->sc_addr, mode, &manual);
323261421Sjhibbits
324261421Sjhibbits	/* Write speed.  */
325261421Sjhibbits	adt746x_write(sc->sc_dev, sc->sc_addr, reg, &buf);
326261421Sjhibbits
327261421Sjhibbits	return (0);
328261421Sjhibbits}
329261421Sjhibbits
330261421Sjhibbitsstatic int
331261421Sjhibbitsadt746x_fan_get_pwm(struct adt746x_fan *fan)
332261421Sjhibbits{
333261421Sjhibbits	uint8_t buf, reg;
334261421Sjhibbits	uint16_t pwm;
335261421Sjhibbits	struct adt746x_softc *sc;
336261421Sjhibbits
337261421Sjhibbits	sc = device_get_softc(fan->dev);
338261421Sjhibbits
339261421Sjhibbits	reg = fan->pwm_reg;
340261421Sjhibbits
341261421Sjhibbits	adt746x_read(sc->sc_dev, sc->sc_addr, reg, &buf);
342261421Sjhibbits
343261421Sjhibbits	pwm = (buf * 39 / 100) + (buf ? 1 : 0);
344261421Sjhibbits	return (pwm);
345261421Sjhibbits}
346261421Sjhibbits
347261421Sjhibbitsstatic int
348261421Sjhibbitsadt746x_fill_fan_prop(device_t dev)
349261421Sjhibbits{
350261421Sjhibbits	phandle_t child;
351261421Sjhibbits	struct adt746x_softc *sc;
352261421Sjhibbits	u_int *id;
353261421Sjhibbits	char *location;
354261421Sjhibbits	int i, id_len, len = 0, location_len, prev_len = 0;
355261421Sjhibbits
356261421Sjhibbits	sc = device_get_softc(dev);
357261421Sjhibbits
358261421Sjhibbits	child = ofw_bus_get_node(dev);
359261421Sjhibbits
360261421Sjhibbits	/* Fill the fan location property. */
361261421Sjhibbits	location_len = OF_getprop_alloc(child, "hwctrl-location", 1, (void **)&location);
362261421Sjhibbits	id_len = OF_getprop_alloc(child, "hwctrl-id", sizeof(cell_t), (void **)&id);
363261421Sjhibbits	if (location_len == -1 || id_len == -1) {
364261421Sjhibbits		free(location, M_OFWPROP);
365261421Sjhibbits		free(id, M_OFWPROP);
366261421Sjhibbits		return 0;
367261421Sjhibbits	}
368261421Sjhibbits
369261421Sjhibbits	/* Fill in all the properties for each fan. */
370261421Sjhibbits	for (i = 0; i < id_len; i++) {
371261421Sjhibbits		strlcpy(sc->sc_fans[i].fan.name, location + len, 32);
372261421Sjhibbits		prev_len = strlen(location + len) + 1;
373261421Sjhibbits		len += prev_len;
374261421Sjhibbits		sc->sc_fans[i].id = id[i];
375261421Sjhibbits		if (id[i] == 6) {
376261421Sjhibbits			sc->sc_fans[i].pwm_reg = ADT746X_PWM1;
377261421Sjhibbits			sc->sc_fans[i].conf_reg = ADT746X_PWM1_CONF;
378261421Sjhibbits		} else if (id[i] == 7) {
379261421Sjhibbits			sc->sc_fans[i].pwm_reg = ADT746X_PWM2;
380261421Sjhibbits			sc->sc_fans[i].conf_reg = ADT746X_PWM2_CONF;
381261421Sjhibbits		} else {
382261421Sjhibbits			sc->sc_fans[i].pwm_reg = ADT746X_PWM1 + i;
383261421Sjhibbits			sc->sc_fans[i].conf_reg = ADT746X_PWM1_CONF + i;
384261421Sjhibbits		}
385261421Sjhibbits		sc->sc_fans[i].dev = sc->sc_dev;
386261421Sjhibbits		sc->sc_fans[i].fan.min_rpm = 5;	/* Percent */
387261421Sjhibbits		sc->sc_fans[i].fan.max_rpm = 100;
388261421Sjhibbits		sc->sc_fans[i].fan.read = NULL;
389261421Sjhibbits		sc->sc_fans[i].fan.set =
390261421Sjhibbits			(int (*)(struct pmac_fan *, int))(adt746x_fan_set_pwm);
391261421Sjhibbits		sc->sc_fans[i].fan.default_rpm = sc->sc_fans[i].fan.max_rpm;
392261421Sjhibbits	}
393261421Sjhibbits	free(location, M_OFWPROP);
394261421Sjhibbits	free(id, M_OFWPROP);
395261421Sjhibbits
396261421Sjhibbits	return (i);
397261421Sjhibbits}
398261421Sjhibbits
399261421Sjhibbitsstatic int
400261421Sjhibbitsadt746x_fill_sensor_prop(device_t dev)
401261421Sjhibbits{
402261421Sjhibbits	phandle_t child, node;
403261421Sjhibbits	struct adt746x_softc *sc;
404261421Sjhibbits	char sens_type[32];
405261421Sjhibbits	int i = 0, reg, sensid;
406261421Sjhibbits
407261421Sjhibbits	sc = device_get_softc(dev);
408261421Sjhibbits
409261421Sjhibbits	child = ofw_bus_get_node(dev);
410261421Sjhibbits
411261421Sjhibbits	/* Fill in the sensor properties for each child. */
412261421Sjhibbits	for (node = OF_child(child); node != 0; node = OF_peer(node)) {
413261421Sjhibbits		if (OF_getprop(node, "sensor-id", &sensid, sizeof(sensid)) == -1)
414261421Sjhibbits		    continue;
415261421Sjhibbits		OF_getprop(node, "location", sc->sc_sensors[i].therm.name, 32);
416261421Sjhibbits		OF_getprop(node, "device_type", sens_type, sizeof(sens_type));
417261421Sjhibbits		if (strcmp(sens_type, "temperature") == 0)
418261421Sjhibbits			sc->sc_sensors[i].type = ADT746X_SENSOR_TEMP;
419261421Sjhibbits		else if (strcmp(sens_type, "voltage") == 0)
420261421Sjhibbits			sc->sc_sensors[i].type = ADT746X_SENSOR_VOLT;
421261421Sjhibbits		else
422261421Sjhibbits			sc->sc_sensors[i].type = ADT746X_SENSOR_SPEED;
423261421Sjhibbits		OF_getprop(node, "reg", &reg, sizeof(reg));
424261421Sjhibbits		OF_getprop(node, "sensor-id", &sensid,
425261421Sjhibbits			sizeof(sensid));
426261421Sjhibbits		/* This is the i2c register of the sensor.  */
427261421Sjhibbits		sc->sc_sensors[i].reg = reg;
428261421Sjhibbits		sc->sc_sensors[i].id = sensid;
429261421Sjhibbits		OF_getprop(node, "zone", &sc->sc_sensors[i].therm.zone,
430261421Sjhibbits			sizeof(sc->sc_sensors[i].therm.zone));
431261421Sjhibbits		sc->sc_sensors[i].dev = dev;
432261421Sjhibbits		sc->sc_sensors[i].therm.read =
433261421Sjhibbits		    (int (*)(struct pmac_therm *))adt746x_sensor_read;
434261421Sjhibbits		if (sc->sc_sensors[i].type == ADT746X_SENSOR_TEMP) {
435261421Sjhibbits		    /* Make up some ranges */
436261421Sjhibbits		    sc->sc_sensors[i].therm.target_temp = 500 + ZERO_C_TO_K;
437261421Sjhibbits		    sc->sc_sensors[i].therm.max_temp = 800 + ZERO_C_TO_K;
438261421Sjhibbits
439261421Sjhibbits		    pmac_thermal_sensor_register(&sc->sc_sensors[i].therm);
440261421Sjhibbits		}
441261421Sjhibbits		i++;
442261421Sjhibbits	}
443261421Sjhibbits
444261421Sjhibbits	return (i);
445261421Sjhibbits}
446261421Sjhibbits
447261421Sjhibbitsstatic int
448261421Sjhibbitsadt746x_fanrpm_sysctl(SYSCTL_HANDLER_ARGS)
449261421Sjhibbits{
450261421Sjhibbits	device_t adt;
451261421Sjhibbits	struct adt746x_softc *sc;
452261421Sjhibbits	struct adt746x_fan *fan;
453261421Sjhibbits	int pwm = 0, error;
454261421Sjhibbits
455261421Sjhibbits	adt = arg1;
456261421Sjhibbits	sc = device_get_softc(adt);
457261421Sjhibbits	fan = &sc->sc_fans[arg2];
458261421Sjhibbits	pwm = adt746x_fan_get_pwm(fan);
459261421Sjhibbits	error = sysctl_handle_int(oidp, &pwm, 0, req);
460261421Sjhibbits
461261421Sjhibbits	if (error || !req->newptr)
462261421Sjhibbits		return (error);
463261421Sjhibbits
464261421Sjhibbits	return (adt746x_fan_set_pwm(fan, pwm));
465261421Sjhibbits}
466261421Sjhibbits
467261421Sjhibbitsstatic void
468261421Sjhibbitsadt746x_attach_fans(device_t dev)
469261421Sjhibbits{
470261421Sjhibbits	struct adt746x_softc *sc;
471261421Sjhibbits	struct sysctl_oid *oid, *fanroot_oid;
472261421Sjhibbits	struct sysctl_ctx_list *ctx;
473261421Sjhibbits	phandle_t child;
474261421Sjhibbits	char sysctl_name[32];
475261421Sjhibbits	int i, j;
476261421Sjhibbits
477261421Sjhibbits	sc = device_get_softc(dev);
478261421Sjhibbits
479261421Sjhibbits	sc->sc_nfans = 0;
480261421Sjhibbits
481261421Sjhibbits	child = ofw_bus_get_node(dev);
482261421Sjhibbits
483261421Sjhibbits	/* Count the actual number of fans. */
484261421Sjhibbits	sc->sc_nfans = adt746x_fill_fan_prop(dev);
485261421Sjhibbits
486261421Sjhibbits	device_printf(dev, "%d fans detected!\n", sc->sc_nfans);
487261421Sjhibbits
488261421Sjhibbits	if (sc->sc_nfans == 0) {
489261421Sjhibbits		device_printf(dev, "WARNING: No fans detected!\n");
490261421Sjhibbits		return;
491261421Sjhibbits	}
492261421Sjhibbits
493261421Sjhibbits	ctx = device_get_sysctl_ctx(dev);
494261421Sjhibbits	fanroot_oid = SYSCTL_ADD_NODE(ctx,
495261421Sjhibbits	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "fans",
496261421Sjhibbits	    CTLFLAG_RD, 0, "ADT Fan Information");
497261421Sjhibbits
498261421Sjhibbits	/* Now we can fill the properties into the allocated struct. */
499261421Sjhibbits	sc->sc_nfans = adt746x_fill_fan_prop(dev);
500261421Sjhibbits
501261421Sjhibbits	/* Register fans with pmac_thermal */
502261421Sjhibbits	for (i = 0; i < sc->sc_nfans; i++)
503261421Sjhibbits		pmac_thermal_fan_register(&sc->sc_fans[i].fan);
504261421Sjhibbits
505261421Sjhibbits	/* Add sysctls for the fans. */
506261421Sjhibbits	for (i = 0; i < sc->sc_nfans; i++) {
507261421Sjhibbits		for (j = 0; j < strlen(sc->sc_fans[i].fan.name); j++) {
508261421Sjhibbits			sysctl_name[j] = tolower(sc->sc_fans[i].fan.name[j]);
509261421Sjhibbits			if (isspace(sysctl_name[j]))
510261421Sjhibbits				sysctl_name[j] = '_';
511261421Sjhibbits		}
512261421Sjhibbits		sysctl_name[j] = 0;
513261421Sjhibbits
514261421Sjhibbits		sc->sc_fans[i].setpoint =
515261421Sjhibbits			adt746x_fan_get_pwm(&sc->sc_fans[i]);
516261421Sjhibbits
517261421Sjhibbits		oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(fanroot_oid),
518261421Sjhibbits		    OID_AUTO, sysctl_name, CTLFLAG_RD, 0, "Fan Information");
519261421Sjhibbits
520261421Sjhibbits		/* I use i to pass the fan id. */
521261421Sjhibbits		SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
522261421Sjhibbits				"pwm", CTLTYPE_INT | CTLFLAG_RW, dev, i,
523261421Sjhibbits				adt746x_fanrpm_sysctl, "I", "Fan PWM in %");
524261421Sjhibbits	}
525261421Sjhibbits
526261421Sjhibbits	/* Dump fan location & type. */
527261421Sjhibbits	if (bootverbose) {
528261421Sjhibbits		for (i = 0; i < sc->sc_nfans; i++) {
529261421Sjhibbits			device_printf(dev, "Fan location: %s",
530261421Sjhibbits				      sc->sc_fans[i].fan.name);
531261421Sjhibbits			device_printf(dev, " id: %d RPM: %d\n",
532261421Sjhibbits				      sc->sc_fans[i].id,
533261421Sjhibbits				      sc->sc_fans[i].setpoint);
534261421Sjhibbits		}
535261421Sjhibbits	}
536261421Sjhibbits}
537261421Sjhibbits
538261421Sjhibbitsstatic int
539261421Sjhibbitsadt746x_sensor_read(struct adt746x_sensor *sens)
540261421Sjhibbits{
541261421Sjhibbits	struct adt746x_softc *sc;
542261421Sjhibbits	uint16_t tmp = 0;
543261421Sjhibbits	uint16_t val;
544261421Sjhibbits	uint8_t temp, data[1], data1[1];
545261421Sjhibbits
546261421Sjhibbits	sc = device_get_softc(sens->dev);
547261421Sjhibbits	if (sens->type != ADT746X_SENSOR_SPEED) {
548261421Sjhibbits		if (adt746x_read(sc->sc_dev, sc->sc_addr, sens->reg,
549261421Sjhibbits				 &temp) < 0)
550261421Sjhibbits			return (-1);
551261421Sjhibbits		if (sens->type == ADT746X_SENSOR_TEMP)
552261421Sjhibbits			tmp = 10 * temp + ZERO_C_TO_K;
553261421Sjhibbits		else
554261421Sjhibbits			tmp = temp;
555261421Sjhibbits	} else {
556261421Sjhibbits		if (adt746x_read(sc->sc_dev, sc->sc_addr, sens->reg,
557261421Sjhibbits				 data) < 0)
558261421Sjhibbits			return (-1);
559261421Sjhibbits		if (adt746x_read(sc->sc_dev, sc->sc_addr, sens->reg + 1,
560261421Sjhibbits				 data1) < 0)
561261421Sjhibbits			return (-1);
562261421Sjhibbits		val = data[0] + (data1[0] << 8);
563261421Sjhibbits		/* A value of 0xffff means the fan is stopped.  */
564261421Sjhibbits		if (val == 0 || val == 0xffff)
565261421Sjhibbits			tmp = 0;
566261421Sjhibbits		else
567261421Sjhibbits			tmp = (90000 * 60) / val;
568261421Sjhibbits	}
569261421Sjhibbits	return (tmp);
570261421Sjhibbits}
571261421Sjhibbits
572261421Sjhibbitsstatic int
573261421Sjhibbitsadt746x_sensor_sysctl(SYSCTL_HANDLER_ARGS)
574261421Sjhibbits{
575261421Sjhibbits	device_t dev;
576261421Sjhibbits	struct adt746x_softc *sc;
577261421Sjhibbits	struct adt746x_sensor *sens;
578261421Sjhibbits	int value, error;
579261421Sjhibbits
580261421Sjhibbits	dev = arg1;
581261421Sjhibbits	sc = device_get_softc(dev);
582261421Sjhibbits	sens = &sc->sc_sensors[arg2];
583261421Sjhibbits
584261421Sjhibbits	value = sens->therm.read(&sens->therm);
585261421Sjhibbits	if (value < 0)
586261421Sjhibbits		return (ENXIO);
587261421Sjhibbits
588261421Sjhibbits	error = sysctl_handle_int(oidp, &value, 0, req);
589261421Sjhibbits
590261421Sjhibbits	return (error);
591261421Sjhibbits}
592261421Sjhibbits
593261421Sjhibbitsstatic void
594261421Sjhibbitsadt746x_attach_sensors(device_t dev)
595261421Sjhibbits{
596261421Sjhibbits	struct adt746x_softc *sc;
597261421Sjhibbits	struct sysctl_oid *oid, *sensroot_oid;
598261421Sjhibbits	struct sysctl_ctx_list *ctx;
599261421Sjhibbits	phandle_t child;
600261421Sjhibbits	char sysctl_name[40];
601261421Sjhibbits	const char *unit;
602261421Sjhibbits	const char *desc;
603261421Sjhibbits	int i, j;
604261421Sjhibbits
605261421Sjhibbits
606261421Sjhibbits	sc = device_get_softc(dev);
607261421Sjhibbits	sc->sc_nsensors = 0;
608261421Sjhibbits	child = ofw_bus_get_node(dev);
609261421Sjhibbits
610261421Sjhibbits	/* Count the actual number of sensors. */
611261421Sjhibbits	sc->sc_nsensors = adt746x_fill_sensor_prop(dev);
612261421Sjhibbits	device_printf(dev, "%d sensors detected!\n", sc->sc_nsensors);
613261421Sjhibbits	if (sc->sc_nsensors == 0) {
614261421Sjhibbits		device_printf(dev, "WARNING: No sensors detected!\n");
615261421Sjhibbits		return;
616261421Sjhibbits	}
617261421Sjhibbits
618261421Sjhibbits	ctx = device_get_sysctl_ctx(dev);
619261421Sjhibbits	sensroot_oid = SYSCTL_ADD_NODE(ctx,
620261421Sjhibbits	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensors",
621261421Sjhibbits	    CTLFLAG_RD, 0, "ADT Sensor Information");
622261421Sjhibbits
623261421Sjhibbits	/* Add the sysctl for the sensors. */
624261421Sjhibbits	for (i = 0; i < sc->sc_nsensors; i++) {
625261421Sjhibbits		for (j = 0; j < strlen(sc->sc_sensors[i].therm.name); j++) {
626261421Sjhibbits			sysctl_name[j] = tolower(sc->sc_sensors[i].therm.name[j]);
627261421Sjhibbits			if (isspace(sysctl_name[j]))
628261421Sjhibbits				sysctl_name[j] = '_';
629261421Sjhibbits		}
630261421Sjhibbits		sysctl_name[j] = 0;
631261421Sjhibbits		oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(sensroot_oid),
632261421Sjhibbits				      OID_AUTO,
633261421Sjhibbits				      sysctl_name, CTLFLAG_RD, 0,
634261421Sjhibbits				      "Sensor Information");
635261421Sjhibbits		if (sc->sc_sensors[i].type == ADT746X_SENSOR_TEMP) {
636261421Sjhibbits			unit = "temp";
637261421Sjhibbits			desc = "sensor unit (C)";
638261421Sjhibbits		} else if (sc->sc_sensors[i].type == ADT746X_SENSOR_VOLT) {
639261421Sjhibbits			unit = "volt";
640261421Sjhibbits			desc = "sensor unit (mV)";
641261421Sjhibbits		} else {
642261421Sjhibbits			unit = "rpm";
643261421Sjhibbits			desc = "sensor unit (RPM)";
644261421Sjhibbits		}
645261421Sjhibbits		/* I use i to pass the sensor id. */
646261421Sjhibbits		SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
647261421Sjhibbits				unit, CTLTYPE_INT | CTLFLAG_RD, dev, i,
648261421Sjhibbits				adt746x_sensor_sysctl,
649261421Sjhibbits				sc->sc_sensors[i].type == ADT746X_SENSOR_TEMP ?
650261421Sjhibbits				"IK" : "I", desc);
651261421Sjhibbits	}
652261421Sjhibbits
653261421Sjhibbits	/* Dump sensor location & type. */
654261421Sjhibbits	if (bootverbose) {
655261421Sjhibbits		for (i = 0; i < sc->sc_nsensors; i++) {
656261421Sjhibbits			device_printf(dev, "Sensor location: %s",
657261421Sjhibbits				      sc->sc_sensors[i].therm.name);
658261421Sjhibbits			device_printf(dev, " type: %d id: %d reg: 0x%x\n",
659261421Sjhibbits				      sc->sc_sensors[i].type,
660261421Sjhibbits				      sc->sc_sensors[i].id,
661261421Sjhibbits				      sc->sc_sensors[i].reg);
662261421Sjhibbits		}
663261421Sjhibbits	}
664261421Sjhibbits}
665