max6690.c revision 222458
1213904Sandreast/*- 2213904Sandreast * Copyright (c) 2010 Andreas Tobler 3213904Sandreast * All rights reserved. 4213904Sandreast * 5213904Sandreast * Redistribution and use in source and binary forms, with or without 6213904Sandreast * modification, are permitted provided that the following conditions 7213904Sandreast * are met: 8213904Sandreast * 1. Redistributions of source code must retain the above copyright 9213904Sandreast * notice, this list of conditions and the following disclaimer. 10213904Sandreast * 2. Redistributions in binary form must reproduce the above copyright 11213904Sandreast * notice, this list of conditions and the following disclaimer in the 12213904Sandreast * documentation and/or other materials provided with the distribution. 13213904Sandreast * 14213904Sandreast * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15213904Sandreast * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16213904Sandreast * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17213904Sandreast * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18213904Sandreast * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19213904Sandreast * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20213904Sandreast * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 21213904Sandreast * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22213904Sandreast * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23213904Sandreast * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24213904Sandreast * SUCH DAMAGE. 25213904Sandreast */ 26213904Sandreast 27213904Sandreast#include <sys/cdefs.h> 28213904Sandreast__FBSDID("$FreeBSD: head/sys/dev/iicbus/max6690.c 222458 2011-05-29 18:35:57Z nwhitehorn $"); 29213904Sandreast 30213904Sandreast#include <sys/param.h> 31213904Sandreast#include <sys/bus.h> 32213904Sandreast#include <sys/systm.h> 33213904Sandreast#include <sys/module.h> 34213904Sandreast#include <sys/callout.h> 35213904Sandreast#include <sys/conf.h> 36213904Sandreast#include <sys/cpu.h> 37213904Sandreast#include <sys/ctype.h> 38213904Sandreast#include <sys/kernel.h> 39213904Sandreast#include <sys/reboot.h> 40213904Sandreast#include <sys/rman.h> 41213904Sandreast#include <sys/sysctl.h> 42213904Sandreast#include <sys/limits.h> 43213904Sandreast 44213904Sandreast#include <machine/bus.h> 45213904Sandreast#include <machine/md_var.h> 46213904Sandreast 47213904Sandreast#include <dev/iicbus/iicbus.h> 48213904Sandreast#include <dev/iicbus/iiconf.h> 49213904Sandreast 50213904Sandreast#include <dev/ofw/openfirm.h> 51213904Sandreast#include <dev/ofw/ofw_bus.h> 52222458Snwhitehorn#include <powerpc/powermac/powermac_thermal.h> 53213904Sandreast 54213904Sandreast#define FCU_ZERO_C_TO_K 2732 55213904Sandreast 56213904Sandreast/* Inlet, Backside, U3 Heatsink sensor: MAX6690. */ 57213904Sandreast 58213904Sandreast#define MAX6690_INT_TEMP 0x0 59213904Sandreast#define MAX6690_EXT_TEMP 0x1 60213904Sandreast#define MAX6690_EEXT_TEMP 0x10 61213904Sandreast#define MAX6690_IEXT_TEMP 0x11 62213904Sandreast#define MAX6690_TEMP_MASK 0xe0 63213904Sandreast 64213904Sandreaststruct max6690_sensor { 65222458Snwhitehorn struct pmac_therm therm; 66222458Snwhitehorn device_t dev; 67222458Snwhitehorn 68213904Sandreast int id; 69213904Sandreast}; 70213904Sandreast 71213904Sandreast/* Regular bus attachment functions */ 72213904Sandreaststatic int max6690_probe(device_t); 73213904Sandreaststatic int max6690_attach(device_t); 74213904Sandreast 75213904Sandreast/* Utility functions */ 76222458Snwhitehornstatic int max6690_sensor_read(struct max6690_sensor *sens); 77213904Sandreaststatic int max6690_sensor_sysctl(SYSCTL_HANDLER_ARGS); 78213904Sandreaststatic void max6690_start(void *xdev); 79213904Sandreaststatic int max6690_read_1(device_t dev, uint32_t addr, uint8_t reg, 80213904Sandreast uint8_t *data); 81213904Sandreast 82213904Sandreaststruct max6690_softc { 83213904Sandreast device_t sc_dev; 84213904Sandreast struct intr_config_hook enum_hook; 85213904Sandreast uint32_t sc_addr; 86213904Sandreast struct max6690_sensor *sc_sensors; 87213904Sandreast int sc_nsensors; 88213904Sandreast}; 89213904Sandreaststatic device_method_t max6690_methods[] = { 90213904Sandreast /* Device interface */ 91213904Sandreast DEVMETHOD(device_probe, max6690_probe), 92213904Sandreast DEVMETHOD(device_attach, max6690_attach), 93213904Sandreast { 0, 0 }, 94213904Sandreast}; 95213904Sandreast 96213904Sandreaststatic driver_t max6690_driver = { 97213904Sandreast "max6690", 98213904Sandreast max6690_methods, 99213904Sandreast sizeof(struct max6690_softc) 100213904Sandreast}; 101213904Sandreast 102213904Sandreaststatic devclass_t max6690_devclass; 103213904Sandreast 104213904SandreastDRIVER_MODULE(max6690, iicbus, max6690_driver, max6690_devclass, 0, 0); 105213904SandreastMALLOC_DEFINE(M_MAX6690, "max6690", "Temp-Monitor MAX6690"); 106213904Sandreast 107213904Sandreaststatic int 108213904Sandreastmax6690_read_1(device_t dev, uint32_t addr, uint8_t reg, uint8_t *data) 109213904Sandreast{ 110213904Sandreast uint8_t buf[4]; 111213904Sandreast 112213904Sandreast struct iic_msg msg[2] = { 113213904Sandreast { addr, IIC_M_WR | IIC_M_NOSTOP, 1, ® }, 114213904Sandreast { addr, IIC_M_RD, 1, buf }, 115213904Sandreast }; 116213904Sandreast 117213904Sandreast if (iicbus_transfer(dev, msg, 2) != 0) { 118213904Sandreast device_printf(dev, "iicbus read failed\n"); 119213904Sandreast return (EIO); 120213904Sandreast } 121213904Sandreast 122213904Sandreast *data = *((uint8_t*)buf); 123213904Sandreast 124213904Sandreast return (0); 125213904Sandreast} 126213904Sandreast 127213904Sandreaststatic int 128213904Sandreastmax6690_probe(device_t dev) 129213904Sandreast{ 130213904Sandreast const char *name, *compatible; 131213904Sandreast struct max6690_softc *sc; 132213904Sandreast 133213904Sandreast name = ofw_bus_get_name(dev); 134213904Sandreast compatible = ofw_bus_get_compat(dev); 135213904Sandreast 136213904Sandreast if (!name) 137213904Sandreast return (ENXIO); 138213904Sandreast 139213904Sandreast if (strcmp(name, "temp-monitor") != 0 || 140213904Sandreast strcmp(compatible, "max6690") != 0) 141213904Sandreast return (ENXIO); 142213904Sandreast 143213904Sandreast sc = device_get_softc(dev); 144213904Sandreast sc->sc_dev = dev; 145213904Sandreast sc->sc_addr = iicbus_get_addr(dev); 146213904Sandreast 147213904Sandreast device_set_desc(dev, "Temp-Monitor MAX6690"); 148213904Sandreast 149213904Sandreast return (0); 150213904Sandreast} 151213904Sandreast 152213904Sandreast/* 153213904Sandreast * This function returns the number of sensors. If we call it the second time 154213904Sandreast * and we have allocated memory for sc->sc_sensors, we fill in the properties. 155213904Sandreast */ 156213904Sandreaststatic int 157213904Sandreastmax6690_fill_sensor_prop(device_t dev) 158213904Sandreast{ 159213904Sandreast phandle_t child; 160213904Sandreast struct max6690_softc *sc; 161213904Sandreast u_int id[8]; 162213904Sandreast char location[96]; 163213904Sandreast int i = 0, j, len = 0, prop_len, prev_len = 0; 164213904Sandreast 165213904Sandreast sc = device_get_softc(dev); 166213904Sandreast 167213904Sandreast child = ofw_bus_get_node(dev); 168213904Sandreast 169213904Sandreast /* Fill the sensor location property. */ 170213904Sandreast prop_len = OF_getprop(child, "hwsensor-location", location, 171213904Sandreast sizeof(location)); 172213904Sandreast while (len < prop_len) { 173213904Sandreast if (sc->sc_sensors != NULL) 174222458Snwhitehorn strcpy(sc->sc_sensors[i].therm.name, location + len); 175213904Sandreast prev_len = strlen(location + len) + 1; 176213904Sandreast len += prev_len; 177213904Sandreast i++; 178213904Sandreast } 179213904Sandreast if (sc->sc_sensors == NULL) 180213904Sandreast return (i); 181213904Sandreast 182213904Sandreast /* Fill the sensor id property. */ 183213904Sandreast prop_len = OF_getprop(child, "hwsensor-id", id, sizeof(id)); 184213904Sandreast for (j = 0; j < i; j++) 185213904Sandreast sc->sc_sensors[j].id = (id[j] & 0xf); 186213904Sandreast 187222458Snwhitehorn /* Fill the sensor zone property. */ 188222458Snwhitehorn prop_len = OF_getprop(child, "hwsensor-zone", id, sizeof(id)); 189222458Snwhitehorn for (j = 0; j < i; j++) 190222458Snwhitehorn sc->sc_sensors[j].therm.zone = id[j]; 191222458Snwhitehorn 192222458Snwhitehorn /* Set up remaining sensor properties */ 193222458Snwhitehorn for (j = 0; j < i; j++) { 194222458Snwhitehorn sc->sc_sensors[j].dev = dev; 195222458Snwhitehorn 196222458Snwhitehorn sc->sc_sensors[j].therm.target_temp = 400 + 2732; 197222458Snwhitehorn sc->sc_sensors[j].therm.max_temp = 800 + 2732; 198222458Snwhitehorn 199222458Snwhitehorn sc->sc_sensors[j].therm.read = 200222458Snwhitehorn (int (*)(struct pmac_therm *))(max6690_sensor_read); 201222458Snwhitehorn } 202222458Snwhitehorn 203213904Sandreast return (i); 204213904Sandreast} 205213904Sandreaststatic int 206213904Sandreastmax6690_attach(device_t dev) 207213904Sandreast{ 208213904Sandreast struct max6690_softc *sc; 209213904Sandreast 210213904Sandreast sc = device_get_softc(dev); 211213904Sandreast 212213904Sandreast sc->enum_hook.ich_func = max6690_start; 213213904Sandreast sc->enum_hook.ich_arg = dev; 214213904Sandreast 215213904Sandreast /* We have to wait until interrupts are enabled. I2C read and write 216213904Sandreast * only works if the interrupts are available. 217213904Sandreast * The unin/i2c is controlled by the htpic on unin. But this is not 218213904Sandreast * the master. The openpic on mac-io is controlling the htpic. 219213904Sandreast * This one gets attached after the mac-io probing and then the 220213904Sandreast * interrupts will be available. 221213904Sandreast */ 222213904Sandreast 223213904Sandreast if (config_intrhook_establish(&sc->enum_hook) != 0) 224213904Sandreast return (ENOMEM); 225213904Sandreast 226213904Sandreast return (0); 227213904Sandreast} 228213904Sandreast 229213904Sandreaststatic void 230213904Sandreastmax6690_start(void *xdev) 231213904Sandreast{ 232213904Sandreast struct max6690_softc *sc; 233213904Sandreast struct sysctl_oid *oid, *sensroot_oid; 234213904Sandreast struct sysctl_ctx_list *ctx; 235213904Sandreast char sysctl_name[32]; 236213904Sandreast int i, j; 237213904Sandreast 238213904Sandreast device_t dev = (device_t)xdev; 239213904Sandreast 240213904Sandreast sc = device_get_softc(dev); 241213904Sandreast 242213904Sandreast sc->sc_nsensors = 0; 243213904Sandreast 244213904Sandreast /* Count the actual number of sensors. */ 245213904Sandreast sc->sc_nsensors = max6690_fill_sensor_prop(dev); 246213904Sandreast 247213904Sandreast device_printf(dev, "%d sensors detected.\n", sc->sc_nsensors); 248213904Sandreast 249213904Sandreast if (sc->sc_nsensors == 0) 250213904Sandreast device_printf(dev, "WARNING: No MAX6690 sensors detected!\n"); 251213904Sandreast 252213904Sandreast sc->sc_sensors = malloc (sc->sc_nsensors * sizeof(struct max6690_sensor), 253213904Sandreast M_MAX6690, M_WAITOK | M_ZERO); 254213904Sandreast 255213904Sandreast ctx = device_get_sysctl_ctx(dev); 256213904Sandreast sensroot_oid = SYSCTL_ADD_NODE(ctx, 257213904Sandreast SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor", 258213904Sandreast CTLFLAG_RD, 0, "MAX6690 Sensor Information"); 259213904Sandreast 260213904Sandreast /* Now we can fill the properties into the allocated struct. */ 261213904Sandreast sc->sc_nsensors = max6690_fill_sensor_prop(dev); 262213904Sandreast 263222458Snwhitehorn /* Register with powermac_thermal */ 264222458Snwhitehorn for (i = 0; i < sc->sc_nsensors; i++) 265222458Snwhitehorn pmac_thermal_sensor_register(&sc->sc_sensors[i].therm); 266222458Snwhitehorn 267213904Sandreast /* Add sysctls for the sensors. */ 268213904Sandreast for (i = 0; i < sc->sc_nsensors; i++) { 269222458Snwhitehorn for (j = 0; j < strlen(sc->sc_sensors[i].therm.name); j++) { 270222458Snwhitehorn sysctl_name[j] = 271222458Snwhitehorn tolower(sc->sc_sensors[i].therm.name[j]); 272213904Sandreast if (isspace(sysctl_name[j])) 273213904Sandreast sysctl_name[j] = '_'; 274213904Sandreast } 275213904Sandreast sysctl_name[j] = 0; 276213904Sandreast 277213904Sandreast oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(sensroot_oid), 278213904Sandreast OID_AUTO, 279213904Sandreast sysctl_name, CTLFLAG_RD, 0, 280213904Sandreast "Sensor Information"); 281213904Sandreast /* I use i to pass the sensor id. */ 282213904Sandreast SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "temp", 283213904Sandreast CTLTYPE_INT | CTLFLAG_RD, dev, i % 2, 284213904Sandreast max6690_sensor_sysctl, "IK", 285213904Sandreast "Sensor Temp in ��C"); 286213904Sandreast 287213904Sandreast } 288213904Sandreast /* Dump sensor location & ID. */ 289213904Sandreast if (bootverbose) { 290213904Sandreast device_printf(dev, "Sensors\n"); 291213904Sandreast for (i = 0; i < sc->sc_nsensors; i++) { 292213904Sandreast device_printf(dev, "Location : %s ID: %d\n", 293222458Snwhitehorn sc->sc_sensors[i].therm.name, 294213904Sandreast sc->sc_sensors[i].id); 295213904Sandreast } 296213904Sandreast } 297213904Sandreast 298213904Sandreast config_intrhook_disestablish(&sc->enum_hook); 299213904Sandreast} 300213904Sandreast 301213904Sandreaststatic int 302222458Snwhitehornmax6690_sensor_read(struct max6690_sensor *sens) 303213904Sandreast{ 304213904Sandreast uint8_t reg_int = 0, reg_ext = 0; 305213904Sandreast uint8_t integer; 306213904Sandreast uint8_t fraction; 307222458Snwhitehorn int temp; 308213904Sandreast struct max6690_softc *sc; 309213904Sandreast 310222458Snwhitehorn sc = device_get_softc(sens->dev); 311213904Sandreast 312213904Sandreast /* The internal sensor id's are even, the external ar odd. */ 313213904Sandreast if ((sens->id % 2) == 0) { 314213904Sandreast reg_int = MAX6690_INT_TEMP; 315213904Sandreast reg_ext = MAX6690_IEXT_TEMP; 316213904Sandreast } else { 317213904Sandreast reg_int = MAX6690_EXT_TEMP; 318213904Sandreast reg_ext = MAX6690_EEXT_TEMP; 319213904Sandreast } 320213904Sandreast 321213904Sandreast max6690_read_1(sc->sc_dev, sc->sc_addr, reg_int, &integer); 322213904Sandreast 323213904Sandreast max6690_read_1(sc->sc_dev, sc->sc_addr, reg_ext, &fraction); 324213904Sandreast 325213904Sandreast fraction &= MAX6690_TEMP_MASK; 326213904Sandreast 327213904Sandreast /* The temperature is in tenth kelvin, the fractional part resolution 328213904Sandreast is 0.125. 329213904Sandreast */ 330222458Snwhitehorn temp = (integer * 10) + (fraction >> 5) * 10 / 8; 331213904Sandreast 332222458Snwhitehorn return (temp); 333213904Sandreast} 334213904Sandreast 335213904Sandreaststatic int 336213904Sandreastmax6690_sensor_sysctl(SYSCTL_HANDLER_ARGS) 337213904Sandreast{ 338213904Sandreast device_t dev; 339213904Sandreast struct max6690_softc *sc; 340213904Sandreast struct max6690_sensor *sens; 341213904Sandreast int value = 0; 342213904Sandreast int error; 343213904Sandreast unsigned int temp; 344213904Sandreast 345213904Sandreast dev = arg1; 346213904Sandreast sc = device_get_softc(dev); 347213904Sandreast sens = &sc->sc_sensors[arg2]; 348213904Sandreast 349222458Snwhitehorn value = max6690_sensor_read(sens); 350222458Snwhitehorn if (value < 0) 351222458Snwhitehorn return (EIO); 352213904Sandreast 353213904Sandreast temp = value + FCU_ZERO_C_TO_K; 354213904Sandreast 355213904Sandreast error = sysctl_handle_int(oidp, &temp, 0, req); 356213904Sandreast 357213904Sandreast return (error); 358213904Sandreast} 359