2 * Copyright (c) 2012 Andreas Tobler
3 * Copyright (c) 2014 Justin Hibbits
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
31 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/module.h>
35 #include <sys/callout.h>
38 #include <sys/ctype.h>
39 #include <sys/kernel.h>
40 #include <sys/reboot.h>
42 #include <sys/sysctl.h>
43 #include <sys/limits.h>
45 #include <machine/bus.h>
46 #include <machine/md_var.h>
48 #include <dev/iicbus/iicbus.h>
49 #include <dev/iicbus/iiconf.h>
51 #include <dev/ofw/openfirm.h>
52 #include <dev/ofw/ofw_bus.h>
53 #include <powerpc/powermac/powermac_thermal.h>
55 /* ADT746X registers. */
56 #define ADT746X_TACH1LOW 0x28
57 #define ADT746X_TACH1HIGH 0x29
58 #define ADT746X_TACH2LOW 0x2a
59 #define ADT746X_TACH2HIGH 0x2b
60 #define ADT746X_PWM1 0x30
61 #define ADT746X_PWM2 0x31
62 #define ADT746X_DEVICE_ID 0x3d
63 #define ADT746X_COMPANY_ID 0x3e
64 #define ADT746X_REV_ID 0x3f
65 #define ADT746X_CONFIG 0x40
66 #define ADT746X_PWM1_CONF 0x5c
67 #define ADT746X_PWM2_CONF 0x5d
68 #define ADT746X_MANUAL_MASK 0xe0
70 #define ADT7460_DEV_ID 0x27
71 #define ADT7467_DEV_ID 0x68
82 struct adt746x_sensor {
83 struct pmac_therm therm;
94 struct adt746x_softc {
96 struct intr_config_hook enum_hook;
98 /* The 7467 supports up to 4 fans, 2 voltage and 3 temperature sensors. */
99 struct adt746x_fan sc_fans[4];
101 struct adt746x_sensor sc_sensors[9];
108 /* Regular bus attachment functions */
110 static int adt746x_probe(device_t);
111 static int adt746x_attach(device_t);
114 /* Utility functions */
115 static void adt746x_attach_fans(device_t dev);
116 static void adt746x_attach_sensors(device_t dev);
117 static int adt746x_fill_fan_prop(device_t dev);
118 static int adt746x_fill_sensor_prop(device_t dev);
120 static int adt746x_fan_set_pwm(struct adt746x_fan *fan, int pwm);
121 static int adt746x_fan_get_pwm(struct adt746x_fan *fan);
122 static int adt746x_sensor_read(struct adt746x_sensor *sens);
123 static void adt746x_start(void *xdev);
125 /* i2c read/write functions. */
126 static int adt746x_write(device_t dev, uint32_t addr, uint8_t reg,
128 static int adt746x_read(device_t dev, uint32_t addr, uint8_t reg,
131 static device_method_t adt746x_methods[] = {
132 /* Device interface */
133 DEVMETHOD(device_probe, adt746x_probe),
134 DEVMETHOD(device_attach, adt746x_attach),
138 static driver_t adt746x_driver = {
141 sizeof(struct adt746x_softc)
144 static devclass_t adt746x_devclass;
146 DRIVER_MODULE(adt746x, iicbus, adt746x_driver, adt746x_devclass, 0, 0);
147 static MALLOC_DEFINE(M_ADT746X, "adt746x", "ADT Sensor Information");
150 /* i2c read/write functions. */
153 adt746x_write(device_t dev, uint32_t addr, uint8_t reg, uint8_t *buff)
158 struct iic_msg msg[] = {
159 {addr, IIC_M_WR, 2, buf }
162 /* Prepare the write msg. */
164 memcpy(buf + 1, buff, 1);
168 if (iicbus_transfer(dev, msg, 1) == 0)
171 device_printf(dev, "iicbus write failed\n");
174 pause("adt746x_write", hz);
180 adt746x_read(device_t dev, uint32_t addr, uint8_t reg, uint8_t *data)
185 struct iic_msg msg[2] = {
186 {addr, IIC_M_WR | IIC_M_NOSTOP, 1, ®},
187 {addr, IIC_M_RD, 1, buf},
192 err = iicbus_transfer(dev, msg, 2);
196 *data = *((uint8_t*)buf);
200 device_printf(dev, "iicbus read failed\n");
203 pause("adt746x_read", hz);
208 adt746x_probe(device_t dev)
210 const char *name, *compatible;
211 struct adt746x_softc *sc;
213 name = ofw_bus_get_name(dev);
214 compatible = ofw_bus_get_compat(dev);
219 if (strcmp(name, "fan") != 0 ||
220 (strcmp(compatible, "adt7460") != 0 &&
221 strcmp(compatible, "adt7467") != 0))
224 sc = device_get_softc(dev);
226 sc->sc_addr = iicbus_get_addr(dev);
228 device_set_desc(dev, "Apple Thermostat Unit ADT746X");
234 adt746x_attach(device_t dev)
236 struct adt746x_softc *sc;
238 sc = device_get_softc(dev);
240 sc->enum_hook.ich_func = adt746x_start;
241 sc->enum_hook.ich_arg = dev;
243 /* We have to wait until interrupts are enabled. I2C read and write
244 * only works if the interrupts are available.
245 * The unin/i2c is controlled by the htpic on unin. But this is not
246 * the master. The openpic on mac-io is controlling the htpic.
247 * This one gets attached after the mac-io probing and then the
248 * interrupts will be available.
251 if (config_intrhook_establish(&sc->enum_hook) != 0)
258 adt746x_start(void *xdev)
260 uint8_t did, cid, rev, conf;
262 struct adt746x_softc *sc;
264 device_t dev = (device_t)xdev;
266 sc = device_get_softc(dev);
268 adt746x_read(sc->sc_dev, sc->sc_addr, ADT746X_DEVICE_ID, &did);
269 adt746x_read(sc->sc_dev, sc->sc_addr, ADT746X_COMPANY_ID, &cid);
270 adt746x_read(sc->sc_dev, sc->sc_addr, ADT746X_REV_ID, &rev);
271 adt746x_read(sc->sc_dev, sc->sc_addr, ADT746X_CONFIG, &conf);
273 device_printf(dev, "Dev ID %#x, Company ID %#x, Rev ID %#x CNF: %#x\n",
274 did, cid, rev, conf);
276 /* We can get the device id either from 'of' properties or from the chip
277 itself. This method makes sure we can read the chip, otherwise
283 /* Start the ADT7460. */
284 if (sc->device_id == ADT7460_DEV_ID)
285 adt746x_write(sc->sc_dev, sc->sc_addr, ADT746X_CONFIG, &conf);
287 /* Detect and attach child devices. */
288 adt746x_attach_fans(dev);
289 adt746x_attach_sensors(dev);
290 config_intrhook_disestablish(&sc->enum_hook);
294 * Sensor and fan management
297 adt746x_fan_set_pwm(struct adt746x_fan *fan, int pwm)
299 uint8_t reg = 0, manual, mode = 0;
300 struct adt746x_softc *sc;
303 sc = device_get_softc(fan->dev);
305 /* Clamp to allowed range */
306 pwm = max(fan->fan.min_rpm, pwm);
307 pwm = min(fan->fan.max_rpm, pwm);
310 mode = fan->conf_reg;
312 /* From the 7460 datasheet:
313 PWM dutycycle can be programmed from 0% (0x00) to 100% (0xFF)
314 in steps of 0.39% (256 steps).
316 buf = (pwm * 100 / 39) - (pwm ? 1 : 0);
320 adt746x_read(sc->sc_dev, sc->sc_addr, mode, &manual);
321 manual |= ADT746X_MANUAL_MASK;
322 adt746x_write(sc->sc_dev, sc->sc_addr, mode, &manual);
325 adt746x_write(sc->sc_dev, sc->sc_addr, reg, &buf);
331 adt746x_fan_get_pwm(struct adt746x_fan *fan)
335 struct adt746x_softc *sc;
337 sc = device_get_softc(fan->dev);
341 adt746x_read(sc->sc_dev, sc->sc_addr, reg, &buf);
343 pwm = (buf * 39 / 100) + (buf ? 1 : 0);
348 adt746x_fill_fan_prop(device_t dev)
351 struct adt746x_softc *sc;
354 int i, id_len, len = 0, location_len, prev_len = 0;
356 sc = device_get_softc(dev);
358 child = ofw_bus_get_node(dev);
360 /* Fill the fan location property. */
361 location_len = OF_getprop_alloc(child, "hwctrl-location", 1, (void **)&location);
362 id_len = OF_getprop_alloc(child, "hwctrl-id", sizeof(cell_t), (void **)&id);
363 if (location_len == -1 || id_len == -1) {
364 free(location, M_OFWPROP);
369 /* Fill in all the properties for each fan. */
370 for (i = 0; i < id_len; i++) {
371 strlcpy(sc->sc_fans[i].fan.name, location + len, 32);
372 prev_len = strlen(location + len) + 1;
374 sc->sc_fans[i].id = id[i];
376 sc->sc_fans[i].pwm_reg = ADT746X_PWM1;
377 sc->sc_fans[i].conf_reg = ADT746X_PWM1_CONF;
378 } else if (id[i] == 7) {
379 sc->sc_fans[i].pwm_reg = ADT746X_PWM2;
380 sc->sc_fans[i].conf_reg = ADT746X_PWM2_CONF;
382 sc->sc_fans[i].pwm_reg = ADT746X_PWM1 + i;
383 sc->sc_fans[i].conf_reg = ADT746X_PWM1_CONF + i;
385 sc->sc_fans[i].dev = sc->sc_dev;
386 sc->sc_fans[i].fan.min_rpm = 5; /* Percent */
387 sc->sc_fans[i].fan.max_rpm = 100;
388 sc->sc_fans[i].fan.read = NULL;
389 sc->sc_fans[i].fan.set =
390 (int (*)(struct pmac_fan *, int))(adt746x_fan_set_pwm);
391 sc->sc_fans[i].fan.default_rpm = sc->sc_fans[i].fan.max_rpm;
393 free(location, M_OFWPROP);
400 adt746x_fill_sensor_prop(device_t dev)
402 phandle_t child, node;
403 struct adt746x_softc *sc;
405 int i = 0, reg, sensid;
407 sc = device_get_softc(dev);
409 child = ofw_bus_get_node(dev);
411 /* Fill in the sensor properties for each child. */
412 for (node = OF_child(child); node != 0; node = OF_peer(node)) {
413 if (OF_getprop(node, "sensor-id", &sensid, sizeof(sensid)) == -1)
415 OF_getprop(node, "location", sc->sc_sensors[i].therm.name, 32);
416 OF_getprop(node, "device_type", sens_type, sizeof(sens_type));
417 if (strcmp(sens_type, "temperature") == 0)
418 sc->sc_sensors[i].type = ADT746X_SENSOR_TEMP;
419 else if (strcmp(sens_type, "voltage") == 0)
420 sc->sc_sensors[i].type = ADT746X_SENSOR_VOLT;
422 sc->sc_sensors[i].type = ADT746X_SENSOR_SPEED;
423 OF_getprop(node, "reg", ®, sizeof(reg));
424 OF_getprop(node, "sensor-id", &sensid,
426 /* This is the i2c register of the sensor. */
427 sc->sc_sensors[i].reg = reg;
428 sc->sc_sensors[i].id = sensid;
429 OF_getprop(node, "zone", &sc->sc_sensors[i].therm.zone,
430 sizeof(sc->sc_sensors[i].therm.zone));
431 sc->sc_sensors[i].dev = dev;
432 sc->sc_sensors[i].therm.read =
433 (int (*)(struct pmac_therm *))adt746x_sensor_read;
434 if (sc->sc_sensors[i].type == ADT746X_SENSOR_TEMP) {
435 /* Make up some ranges */
436 sc->sc_sensors[i].therm.target_temp = 500 + ZERO_C_TO_K;
437 sc->sc_sensors[i].therm.max_temp = 800 + ZERO_C_TO_K;
439 pmac_thermal_sensor_register(&sc->sc_sensors[i].therm);
448 adt746x_fanrpm_sysctl(SYSCTL_HANDLER_ARGS)
451 struct adt746x_softc *sc;
452 struct adt746x_fan *fan;
456 sc = device_get_softc(adt);
457 fan = &sc->sc_fans[arg2];
458 pwm = adt746x_fan_get_pwm(fan);
459 error = sysctl_handle_int(oidp, &pwm, 0, req);
461 if (error || !req->newptr)
464 return (adt746x_fan_set_pwm(fan, pwm));
468 adt746x_attach_fans(device_t dev)
470 struct adt746x_softc *sc;
471 struct sysctl_oid *oid, *fanroot_oid;
472 struct sysctl_ctx_list *ctx;
474 char sysctl_name[32];
477 sc = device_get_softc(dev);
481 child = ofw_bus_get_node(dev);
483 /* Count the actual number of fans. */
484 sc->sc_nfans = adt746x_fill_fan_prop(dev);
486 device_printf(dev, "%d fans detected!\n", sc->sc_nfans);
488 if (sc->sc_nfans == 0) {
489 device_printf(dev, "WARNING: No fans detected!\n");
493 ctx = device_get_sysctl_ctx(dev);
494 fanroot_oid = SYSCTL_ADD_NODE(ctx,
495 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "fans",
496 CTLFLAG_RD, 0, "ADT Fan Information");
498 /* Now we can fill the properties into the allocated struct. */
499 sc->sc_nfans = adt746x_fill_fan_prop(dev);
501 /* Register fans with pmac_thermal */
502 for (i = 0; i < sc->sc_nfans; i++)
503 pmac_thermal_fan_register(&sc->sc_fans[i].fan);
505 /* Add sysctls for the fans. */
506 for (i = 0; i < sc->sc_nfans; i++) {
507 for (j = 0; j < strlen(sc->sc_fans[i].fan.name); j++) {
508 sysctl_name[j] = tolower(sc->sc_fans[i].fan.name[j]);
509 if (isspace(sysctl_name[j]))
510 sysctl_name[j] = '_';
514 sc->sc_fans[i].setpoint =
515 adt746x_fan_get_pwm(&sc->sc_fans[i]);
517 oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(fanroot_oid),
518 OID_AUTO, sysctl_name, CTLFLAG_RD, 0, "Fan Information");
520 /* I use i to pass the fan id. */
521 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
522 "pwm", CTLTYPE_INT | CTLFLAG_RW, dev, i,
523 adt746x_fanrpm_sysctl, "I", "Fan PWM in %");
526 /* Dump fan location & type. */
528 for (i = 0; i < sc->sc_nfans; i++) {
529 device_printf(dev, "Fan location: %s",
530 sc->sc_fans[i].fan.name);
531 device_printf(dev, " id: %d RPM: %d\n",
533 sc->sc_fans[i].setpoint);
539 adt746x_sensor_read(struct adt746x_sensor *sens)
541 struct adt746x_softc *sc;
544 uint8_t temp, data[1], data1[1];
546 sc = device_get_softc(sens->dev);
547 if (sens->type != ADT746X_SENSOR_SPEED) {
548 if (adt746x_read(sc->sc_dev, sc->sc_addr, sens->reg,
551 if (sens->type == ADT746X_SENSOR_TEMP)
552 tmp = 10 * temp + ZERO_C_TO_K;
556 if (adt746x_read(sc->sc_dev, sc->sc_addr, sens->reg,
559 if (adt746x_read(sc->sc_dev, sc->sc_addr, sens->reg + 1,
562 val = data[0] + (data1[0] << 8);
563 /* A value of 0xffff means the fan is stopped. */
564 if (val == 0 || val == 0xffff)
567 tmp = (90000 * 60) / val;
573 adt746x_sensor_sysctl(SYSCTL_HANDLER_ARGS)
576 struct adt746x_softc *sc;
577 struct adt746x_sensor *sens;
581 sc = device_get_softc(dev);
582 sens = &sc->sc_sensors[arg2];
584 value = sens->therm.read(&sens->therm);
588 error = sysctl_handle_int(oidp, &value, 0, req);
594 adt746x_attach_sensors(device_t dev)
596 struct adt746x_softc *sc;
597 struct sysctl_oid *oid, *sensroot_oid;
598 struct sysctl_ctx_list *ctx;
600 char sysctl_name[40];
606 sc = device_get_softc(dev);
608 child = ofw_bus_get_node(dev);
610 /* Count the actual number of sensors. */
611 sc->sc_nsensors = adt746x_fill_sensor_prop(dev);
612 device_printf(dev, "%d sensors detected!\n", sc->sc_nsensors);
613 if (sc->sc_nsensors == 0) {
614 device_printf(dev, "WARNING: No sensors detected!\n");
618 ctx = device_get_sysctl_ctx(dev);
619 sensroot_oid = SYSCTL_ADD_NODE(ctx,
620 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensors",
621 CTLFLAG_RD, 0, "ADT Sensor Information");
623 /* Add the sysctl for the sensors. */
624 for (i = 0; i < sc->sc_nsensors; i++) {
625 for (j = 0; j < strlen(sc->sc_sensors[i].therm.name); j++) {
626 sysctl_name[j] = tolower(sc->sc_sensors[i].therm.name[j]);
627 if (isspace(sysctl_name[j]))
628 sysctl_name[j] = '_';
631 oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(sensroot_oid),
633 sysctl_name, CTLFLAG_RD, 0,
634 "Sensor Information");
635 if (sc->sc_sensors[i].type == ADT746X_SENSOR_TEMP) {
637 desc = "sensor unit (C)";
638 } else if (sc->sc_sensors[i].type == ADT746X_SENSOR_VOLT) {
640 desc = "sensor unit (mV)";
643 desc = "sensor unit (RPM)";
645 /* I use i to pass the sensor id. */
646 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
647 unit, CTLTYPE_INT | CTLFLAG_RD, dev, i,
648 adt746x_sensor_sysctl,
649 sc->sc_sensors[i].type == ADT746X_SENSOR_TEMP ?
653 /* Dump sensor location & type. */
655 for (i = 0; i < sc->sc_nsensors; i++) {
656 device_printf(dev, "Sensor location: %s",
657 sc->sc_sensors[i].therm.name);
658 device_printf(dev, " type: %d id: %d reg: 0x%x\n",
659 sc->sc_sensors[i].type,
660 sc->sc_sensors[i].id,
661 sc->sc_sensors[i].reg);