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$"); 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/* Drivebay sensor: LM75/DS1775. */ 55213904Sandreast#define DS1775_TEMP 0x0 56213904Sandreast 57213904Sandreast/* Regular bus attachment functions */ 58213904Sandreaststatic int ds1775_probe(device_t); 59213904Sandreaststatic int ds1775_attach(device_t); 60213904Sandreast 61222458Snwhitehornstruct ds1775_softc { 62222458Snwhitehorn struct pmac_therm sc_sensor; 63222458Snwhitehorn device_t sc_dev; 64222458Snwhitehorn struct intr_config_hook enum_hook; 65222458Snwhitehorn uint32_t sc_addr; 66222458Snwhitehorn}; 67222458Snwhitehorn 68213904Sandreast/* Utility functions */ 69222458Snwhitehornstatic int ds1775_sensor_read(struct ds1775_softc *sc); 70213904Sandreaststatic int ds1775_sensor_sysctl(SYSCTL_HANDLER_ARGS); 71213904Sandreaststatic void ds1775_start(void *xdev); 72213904Sandreaststatic int ds1775_read_2(device_t dev, uint32_t addr, uint8_t reg, 73213904Sandreast uint16_t *data); 74213904Sandreast 75213904Sandreaststatic device_method_t ds1775_methods[] = { 76213904Sandreast /* Device interface */ 77213904Sandreast DEVMETHOD(device_probe, ds1775_probe), 78213904Sandreast DEVMETHOD(device_attach, ds1775_attach), 79213904Sandreast { 0, 0 }, 80213904Sandreast}; 81213904Sandreast 82213904Sandreaststatic driver_t ds1775_driver = { 83213904Sandreast "ds1775", 84213904Sandreast ds1775_methods, 85213904Sandreast sizeof(struct ds1775_softc) 86213904Sandreast}; 87213904Sandreast 88213904Sandreaststatic devclass_t ds1775_devclass; 89213904Sandreast 90232400SandreastDRIVER_MODULE(ds1775, iicbus, ds1775_driver, ds1775_devclass, 0, 0); 91213904Sandreast 92213904Sandreaststatic int 93213904Sandreastds1775_read_2(device_t dev, uint32_t addr, uint8_t reg, uint16_t *data) 94213904Sandreast{ 95213904Sandreast uint8_t buf[4]; 96222658Sandreast int err, try = 0; 97213904Sandreast 98213904Sandreast struct iic_msg msg[2] = { 99213904Sandreast { addr, IIC_M_WR | IIC_M_NOSTOP, 1, ® }, 100213904Sandreast { addr, IIC_M_RD, 2, buf }, 101213904Sandreast }; 102213904Sandreast 103222658Sandreast for (;;) 104222658Sandreast { 105222658Sandreast err = iicbus_transfer(dev, msg, 2); 106222658Sandreast if (err != 0) 107222658Sandreast goto retry; 108222658Sandreast 109222658Sandreast *data = *((uint16_t*)buf); 110222658Sandreast return (0); 111222658Sandreast retry: 112222658Sandreast if (++try > 5) { 113222658Sandreast device_printf(dev, "iicbus read failed\n"); 114222658Sandreast return (-1); 115222658Sandreast } 116222658Sandreast pause("ds1775_read_2", hz); 117213904Sandreast } 118213904Sandreast} 119213904Sandreast 120213904Sandreaststatic int 121213904Sandreastds1775_probe(device_t dev) 122213904Sandreast{ 123213904Sandreast const char *name, *compatible; 124213904Sandreast struct ds1775_softc *sc; 125213904Sandreast 126213904Sandreast name = ofw_bus_get_name(dev); 127213904Sandreast compatible = ofw_bus_get_compat(dev); 128213904Sandreast 129213904Sandreast if (!name) 130213904Sandreast return (ENXIO); 131213904Sandreast 132213904Sandreast if (strcmp(name, "temp-monitor") != 0 || 133216360Sandreast (strcmp(compatible, "ds1775") != 0 && 134216360Sandreast strcmp(compatible, "lm75") != 0)) 135213904Sandreast return (ENXIO); 136213904Sandreast 137213904Sandreast sc = device_get_softc(dev); 138213904Sandreast sc->sc_dev = dev; 139213904Sandreast sc->sc_addr = iicbus_get_addr(dev); 140213904Sandreast 141216360Sandreast device_set_desc(dev, "Temp-Monitor DS1775"); 142213904Sandreast 143213904Sandreast return (0); 144213904Sandreast} 145213904Sandreast 146213904Sandreaststatic int 147213904Sandreastds1775_attach(device_t dev) 148213904Sandreast{ 149213904Sandreast struct ds1775_softc *sc; 150213904Sandreast 151213904Sandreast sc = device_get_softc(dev); 152213904Sandreast 153213904Sandreast sc->enum_hook.ich_func = ds1775_start; 154213904Sandreast sc->enum_hook.ich_arg = dev; 155213904Sandreast 156213904Sandreast /* We have to wait until interrupts are enabled. I2C read and write 157213904Sandreast * only works if the interrupts are available. 158213904Sandreast * The unin/i2c is controlled by the htpic on unin. But this is not 159213904Sandreast * the master. The openpic on mac-io is controlling the htpic. 160213904Sandreast * This one gets attached after the mac-io probing and then the 161213904Sandreast * interrupts will be available. 162213904Sandreast */ 163213904Sandreast 164213904Sandreast if (config_intrhook_establish(&sc->enum_hook) != 0) 165213904Sandreast return (ENOMEM); 166213904Sandreast 167213904Sandreast return (0); 168213904Sandreast} 169213904Sandreast 170213904Sandreaststatic void 171213904Sandreastds1775_start(void *xdev) 172213904Sandreast{ 173213904Sandreast phandle_t child; 174213904Sandreast struct ds1775_softc *sc; 175239399Sandreast struct sysctl_oid *oid, *sensroot_oid; 176213904Sandreast struct sysctl_ctx_list *ctx; 177217560Sandreast ssize_t plen; 178213904Sandreast int i; 179213904Sandreast char sysctl_name[40], sysctl_desc[40]; 180213904Sandreast 181213904Sandreast device_t dev = (device_t)xdev; 182213904Sandreast 183213904Sandreast sc = device_get_softc(dev); 184213904Sandreast 185213904Sandreast child = ofw_bus_get_node(dev); 186213904Sandreast 187213904Sandreast ctx = device_get_sysctl_ctx(dev); 188239399Sandreast sensroot_oid = SYSCTL_ADD_NODE(ctx, 189239399Sandreast SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor", 190239399Sandreast CTLFLAG_RD, 0, "DS1775 Sensor Information"); 191213904Sandreast 192222658Sandreast if (OF_getprop(child, "hwsensor-zone", &sc->sc_sensor.zone, 193222658Sandreast sizeof(int)) < 0) 194222658Sandreast sc->sc_sensor.zone = 0; 195222658Sandreast 196222458Snwhitehorn plen = OF_getprop(child, "hwsensor-location", sc->sc_sensor.name, 197222458Snwhitehorn sizeof(sc->sc_sensor.name)); 198213904Sandreast 199217560Sandreast if (plen == -1) { 200217560Sandreast strcpy(sysctl_name, "sensor"); 201217560Sandreast } else { 202222458Snwhitehorn for (i = 0; i < strlen(sc->sc_sensor.name); i++) { 203222458Snwhitehorn sysctl_name[i] = tolower(sc->sc_sensor.name[i]); 204217560Sandreast if (isspace(sysctl_name[i])) 205217560Sandreast sysctl_name[i] = '_'; 206217560Sandreast } 207217560Sandreast sysctl_name[i] = 0; 208213904Sandreast } 209213904Sandreast 210222458Snwhitehorn /* Make up target temperatures. These are low, for the drive bay. */ 211222658Sandreast if (sc->sc_sensor.zone == 0) { 212222673Sandreast sc->sc_sensor.target_temp = 500 + ZERO_C_TO_K; 213222673Sandreast sc->sc_sensor.max_temp = 600 + ZERO_C_TO_K; 214222658Sandreast } 215222658Sandreast else { 216222673Sandreast sc->sc_sensor.target_temp = 300 + ZERO_C_TO_K; 217222673Sandreast sc->sc_sensor.max_temp = 600 + ZERO_C_TO_K; 218222658Sandreast } 219222458Snwhitehorn 220222458Snwhitehorn sc->sc_sensor.read = 221222458Snwhitehorn (int (*)(struct pmac_therm *sc))(ds1775_sensor_read); 222222458Snwhitehorn pmac_thermal_sensor_register(&sc->sc_sensor); 223222458Snwhitehorn 224239399Sandreast sprintf(sysctl_desc,"%s %s", sc->sc_sensor.name, "(C)"); 225239399Sandreast oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(sensroot_oid), 226239399Sandreast OID_AUTO, sysctl_name, CTLFLAG_RD, 0, 227239399Sandreast "Sensor Information"); 228239399Sandreast SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "temp", 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 */ 251222673Sandreast return (((int16_t)(read) >> 7) * 5 + 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