ds1775.c revision 222658
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/ds1775.c 222658 2011-06-03 18:58:32Z andreast $"); 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/* Drivebay sensor: LM75/DS1775. */ 57213904Sandreast#define DS1775_TEMP 0x0 58213904Sandreast 59213904Sandreast/* Regular bus attachment functions */ 60213904Sandreaststatic int ds1775_probe(device_t); 61213904Sandreaststatic int ds1775_attach(device_t); 62213904Sandreast 63222458Snwhitehornstruct ds1775_softc { 64222458Snwhitehorn struct pmac_therm sc_sensor; 65222458Snwhitehorn device_t sc_dev; 66222458Snwhitehorn struct intr_config_hook enum_hook; 67222458Snwhitehorn uint32_t sc_addr; 68222458Snwhitehorn}; 69222458Snwhitehorn 70213904Sandreast/* Utility functions */ 71222458Snwhitehornstatic int ds1775_sensor_read(struct ds1775_softc *sc); 72213904Sandreaststatic int ds1775_sensor_sysctl(SYSCTL_HANDLER_ARGS); 73213904Sandreaststatic void ds1775_start(void *xdev); 74213904Sandreaststatic int ds1775_read_2(device_t dev, uint32_t addr, uint8_t reg, 75213904Sandreast uint16_t *data); 76213904Sandreast 77213904Sandreaststatic device_method_t ds1775_methods[] = { 78213904Sandreast /* Device interface */ 79213904Sandreast DEVMETHOD(device_probe, ds1775_probe), 80213904Sandreast DEVMETHOD(device_attach, ds1775_attach), 81213904Sandreast { 0, 0 }, 82213904Sandreast}; 83213904Sandreast 84213904Sandreaststatic driver_t ds1775_driver = { 85213904Sandreast "ds1775", 86213904Sandreast ds1775_methods, 87213904Sandreast sizeof(struct ds1775_softc) 88213904Sandreast}; 89213904Sandreast 90213904Sandreaststatic devclass_t ds1775_devclass; 91213904Sandreast 92213904SandreastDRIVER_MODULE(ds1755, iicbus, ds1775_driver, ds1775_devclass, 0, 0); 93213904Sandreast 94213904Sandreaststatic int 95213904Sandreastds1775_read_2(device_t dev, uint32_t addr, uint8_t reg, uint16_t *data) 96213904Sandreast{ 97213904Sandreast uint8_t buf[4]; 98222658Sandreast int err, try = 0; 99213904Sandreast 100213904Sandreast struct iic_msg msg[2] = { 101213904Sandreast { addr, IIC_M_WR | IIC_M_NOSTOP, 1, ® }, 102213904Sandreast { addr, IIC_M_RD, 2, buf }, 103213904Sandreast }; 104213904Sandreast 105222658Sandreast for (;;) 106222658Sandreast { 107222658Sandreast err = iicbus_transfer(dev, msg, 2); 108222658Sandreast if (err != 0) 109222658Sandreast goto retry; 110222658Sandreast 111222658Sandreast *data = *((uint16_t*)buf); 112222658Sandreast return (0); 113222658Sandreast retry: 114222658Sandreast if (++try > 5) { 115222658Sandreast device_printf(dev, "iicbus read failed\n"); 116222658Sandreast return (-1); 117222658Sandreast } 118222658Sandreast pause("ds1775_read_2", hz); 119213904Sandreast } 120213904Sandreast} 121213904Sandreast 122213904Sandreaststatic int 123213904Sandreastds1775_probe(device_t dev) 124213904Sandreast{ 125213904Sandreast const char *name, *compatible; 126213904Sandreast struct ds1775_softc *sc; 127213904Sandreast 128213904Sandreast name = ofw_bus_get_name(dev); 129213904Sandreast compatible = ofw_bus_get_compat(dev); 130213904Sandreast 131213904Sandreast if (!name) 132213904Sandreast return (ENXIO); 133213904Sandreast 134213904Sandreast if (strcmp(name, "temp-monitor") != 0 || 135216360Sandreast (strcmp(compatible, "ds1775") != 0 && 136216360Sandreast strcmp(compatible, "lm75") != 0)) 137213904Sandreast return (ENXIO); 138213904Sandreast 139213904Sandreast sc = device_get_softc(dev); 140213904Sandreast sc->sc_dev = dev; 141213904Sandreast sc->sc_addr = iicbus_get_addr(dev); 142213904Sandreast 143216360Sandreast device_set_desc(dev, "Temp-Monitor DS1775"); 144213904Sandreast 145213904Sandreast return (0); 146213904Sandreast} 147213904Sandreast 148213904Sandreaststatic int 149213904Sandreastds1775_attach(device_t dev) 150213904Sandreast{ 151213904Sandreast struct ds1775_softc *sc; 152213904Sandreast 153213904Sandreast sc = device_get_softc(dev); 154213904Sandreast 155213904Sandreast sc->enum_hook.ich_func = ds1775_start; 156213904Sandreast sc->enum_hook.ich_arg = dev; 157213904Sandreast 158213904Sandreast /* We have to wait until interrupts are enabled. I2C read and write 159213904Sandreast * only works if the interrupts are available. 160213904Sandreast * The unin/i2c is controlled by the htpic on unin. But this is not 161213904Sandreast * the master. The openpic on mac-io is controlling the htpic. 162213904Sandreast * This one gets attached after the mac-io probing and then the 163213904Sandreast * interrupts will be available. 164213904Sandreast */ 165213904Sandreast 166213904Sandreast if (config_intrhook_establish(&sc->enum_hook) != 0) 167213904Sandreast return (ENOMEM); 168213904Sandreast 169213904Sandreast return (0); 170213904Sandreast} 171213904Sandreast 172213904Sandreaststatic void 173213904Sandreastds1775_start(void *xdev) 174213904Sandreast{ 175213904Sandreast phandle_t child; 176213904Sandreast struct ds1775_softc *sc; 177213904Sandreast struct sysctl_oid *sensroot_oid; 178213904Sandreast struct sysctl_ctx_list *ctx; 179217560Sandreast ssize_t plen; 180213904Sandreast int i; 181213904Sandreast char sysctl_name[40], sysctl_desc[40]; 182213904Sandreast const char *units; 183213904Sandreast 184213904Sandreast device_t dev = (device_t)xdev; 185213904Sandreast 186213904Sandreast sc = device_get_softc(dev); 187213904Sandreast 188213904Sandreast child = ofw_bus_get_node(dev); 189213904Sandreast 190213904Sandreast ctx = device_get_sysctl_ctx(dev); 191213904Sandreast sensroot_oid = device_get_sysctl_tree(dev); 192213904Sandreast 193222658Sandreast if (OF_getprop(child, "hwsensor-zone", &sc->sc_sensor.zone, 194222658Sandreast sizeof(int)) < 0) 195222658Sandreast sc->sc_sensor.zone = 0; 196222658Sandreast 197222458Snwhitehorn plen = OF_getprop(child, "hwsensor-location", sc->sc_sensor.name, 198222458Snwhitehorn sizeof(sc->sc_sensor.name)); 199213904Sandreast units = "C"; 200213904Sandreast 201217560Sandreast if (plen == -1) { 202217560Sandreast strcpy(sysctl_name, "sensor"); 203217560Sandreast } else { 204222458Snwhitehorn for (i = 0; i < strlen(sc->sc_sensor.name); i++) { 205222458Snwhitehorn sysctl_name[i] = tolower(sc->sc_sensor.name[i]); 206217560Sandreast if (isspace(sysctl_name[i])) 207217560Sandreast sysctl_name[i] = '_'; 208217560Sandreast } 209217560Sandreast sysctl_name[i] = 0; 210213904Sandreast } 211213904Sandreast 212222458Snwhitehorn /* Make up target temperatures. These are low, for the drive bay. */ 213222658Sandreast if (sc->sc_sensor.zone == 0) { 214222658Sandreast sc->sc_sensor.target_temp = 500 + FCU_ZERO_C_TO_K; 215222658Sandreast sc->sc_sensor.max_temp = 600 + FCU_ZERO_C_TO_K; 216222658Sandreast } 217222658Sandreast else { 218222658Sandreast sc->sc_sensor.target_temp = 300 + FCU_ZERO_C_TO_K; 219222658Sandreast sc->sc_sensor.max_temp = 600 + FCU_ZERO_C_TO_K; 220222658Sandreast } 221222458Snwhitehorn 222222458Snwhitehorn sc->sc_sensor.read = 223222458Snwhitehorn (int (*)(struct pmac_therm *sc))(ds1775_sensor_read); 224222458Snwhitehorn pmac_thermal_sensor_register(&sc->sc_sensor); 225222458Snwhitehorn 226222458Snwhitehorn sprintf(sysctl_desc,"%s (%s)", sc->sc_sensor.name, units); 227213904Sandreast SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sensroot_oid), OID_AUTO, 228213904Sandreast sysctl_name, 229213904Sandreast CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, 230213904Sandreast 0, ds1775_sensor_sysctl, "IK", sysctl_desc); 231213904Sandreast 232213904Sandreast config_intrhook_disestablish(&sc->enum_hook); 233213904Sandreast} 234213904Sandreast 235213904Sandreaststatic int 236222458Snwhitehornds1775_sensor_read(struct ds1775_softc *sc) 237213904Sandreast{ 238213904Sandreast uint16_t buf[2]; 239213904Sandreast uint16_t read; 240222658Sandreast int err; 241213904Sandreast 242222658Sandreast err = ds1775_read_2(sc->sc_dev, sc->sc_addr, DS1775_TEMP, buf); 243222658Sandreast if (err < 0) 244222658Sandreast return (-1); 245213904Sandreast 246213904Sandreast read = *((int16_t *)buf); 247213904Sandreast 248213904Sandreast /* The default mode of the ADC is 9 bit, the resolution is 0.5 C per 249213904Sandreast bit. The temperature is in tenth kelvin. 250213904Sandreast */ 251222458Snwhitehorn return (((int16_t)(read) >> 7) * 5 + FCU_ZERO_C_TO_K); 252222458Snwhitehorn} 253213904Sandreast 254213904Sandreaststatic int 255213904Sandreastds1775_sensor_sysctl(SYSCTL_HANDLER_ARGS) 256213904Sandreast{ 257213904Sandreast device_t dev; 258213904Sandreast struct ds1775_softc *sc; 259213904Sandreast int error; 260213904Sandreast unsigned int temp; 261213904Sandreast 262213904Sandreast dev = arg1; 263213904Sandreast sc = device_get_softc(dev); 264213904Sandreast 265222458Snwhitehorn temp = ds1775_sensor_read(sc); 266222658Sandreast if (temp < 0) 267222658Sandreast return (EIO); 268213904Sandreast 269213904Sandreast error = sysctl_handle_int(oidp, &temp, 0, req); 270213904Sandreast 271213904Sandreast return (error); 272213904Sandreast} 273