Home | History | Annotate | Line # | Download | only in i2c
lm75.c revision 1.27
      1 /*	$NetBSD: lm75.c,v 1.27 2016/01/01 20:13:50 jdc Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2003 Wasabi Systems, Inc.
      5  * All rights reserved.
      6  *
      7  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in the
     16  *    documentation and/or other materials provided with the distribution.
     17  * 3. All advertising materials mentioning features or use of this software
     18  *    must display the following acknowledgement:
     19  *      This product includes software developed for the NetBSD Project by
     20  *      Wasabi Systems, Inc.
     21  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
     22  *    or promote products derived from this software without specific prior
     23  *    written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
     26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
     29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     35  * POSSIBILITY OF SUCH DAMAGE.
     36  */
     37 
     38 #include <sys/cdefs.h>
     39 __KERNEL_RCSID(0, "$NetBSD: lm75.c,v 1.27 2016/01/01 20:13:50 jdc Exp $");
     40 
     41 #include <sys/param.h>
     42 #include <sys/systm.h>
     43 #include <sys/device.h>
     44 #include <sys/kernel.h>
     45 #include <sys/sysctl.h>
     46 
     47 #include <dev/sysmon/sysmonvar.h>
     48 
     49 #include <dev/i2c/i2cvar.h>
     50 #include <dev/i2c/lm75reg.h>
     51 
     52 struct lmtemp_softc {
     53 	device_t sc_dev;
     54 	i2c_tag_t sc_tag;
     55 	int sc_address;
     56 
     57 	struct sysmon_envsys *sc_sme;
     58 	envsys_data_t sc_sensor;
     59 	int sc_tmax;
     60 
     61 	uint32_t (*sc_lmtemp_decode)(const uint8_t *, int);
     62 };
     63 
     64 static int  lmtemp_match(device_t, cfdata_t, void *);
     65 static void lmtemp_attach(device_t, device_t, void *);
     66 
     67 CFATTACH_DECL_NEW(lmtemp, sizeof(struct lmtemp_softc),
     68 	lmtemp_match, lmtemp_attach, NULL, NULL);
     69 
     70 static void	lmtemp_refresh(struct sysmon_envsys *, envsys_data_t *);
     71 
     72 static int	lmtemp_config_write(struct lmtemp_softc *, uint8_t);
     73 static int	lmtemp_temp_write(struct lmtemp_softc *, int, uint16_t);
     74 static int	lmtemp_temp_read(struct lmtemp_softc *, uint8_t, uint32_t *,
     75 				int);
     76 static uint32_t lmtemp_decode_lm75(const uint8_t *, int);
     77 static uint32_t lmtemp_decode_ds75(const uint8_t *, int);
     78 static uint32_t lmtemp_decode_lm77(const uint8_t *, int);
     79 
     80 static void	lmtemp_setup_sysctl(struct lmtemp_softc *);
     81 static int	sysctl_lm75_temp(SYSCTLFN_ARGS);
     82 
     83 static const char * lmtemp_compats[] = {
     84 	"i2c-lm75",
     85 	/*
     86 	 * see XXX in _attach() below: add code once non-lm75 matches are
     87 	 * added here!
     88 	 */
     89 	NULL
     90 };
     91 
     92 enum {
     93 	lmtemp_lm75 = 0,
     94 	lmtemp_ds75,
     95 	lmtemp_lm77,
     96 };
     97 static const struct {
     98 	int lmtemp_type;
     99 	const char *lmtemp_name;
    100 	int lmtemp_addrmask;
    101 	int lmtemp_addr;
    102 	uint32_t (*lmtemp_decode)(const uint8_t *, int);
    103 } lmtemptbl[] = {
    104 	{ lmtemp_lm75,	"LM75",
    105 	    LM75_ADDRMASK,	LM75_ADDR,	lmtemp_decode_lm75 },
    106 	{ lmtemp_ds75,	"DS75",
    107 	    LM75_ADDRMASK,	LM75_ADDR,	lmtemp_decode_ds75 },
    108 	{ lmtemp_lm77,	"LM77",
    109 	    LM77_ADDRMASK,	LM77_ADDR,	lmtemp_decode_lm77 },
    110 
    111 	{ -1,		NULL,
    112 	    0,			0,		NULL }
    113 };
    114 
    115 static int
    116 lmtemp_match(device_t parent, cfdata_t cf, void *aux)
    117 {
    118 	struct i2c_attach_args *ia = aux;
    119 	int i;
    120 
    121 	if (ia->ia_name == NULL) {
    122 		/*
    123 		 * Indirect config - not much we can do!
    124 		 */
    125 		for (i = 0; lmtemptbl[i].lmtemp_type != -1 ; i++)
    126 			if (lmtemptbl[i].lmtemp_type == cf->cf_flags)
    127 				break;
    128 		if (lmtemptbl[i].lmtemp_type == -1)
    129 			return 0;
    130 
    131 		if ((ia->ia_addr & lmtemptbl[i].lmtemp_addrmask) ==
    132 		    lmtemptbl[i].lmtemp_addr)
    133 			return 1;
    134 	} else {
    135 		/*
    136 		 * Direct config - match via the list of compatible
    137 		 * hardware or simply match the device name.
    138 		 */
    139 		if (ia->ia_ncompat > 0) {
    140 			if (iic_compat_match(ia, lmtemp_compats))
    141 				return 1;
    142 		} else {
    143 			if (strcmp(ia->ia_name, "lmtemp") == 0)
    144 				return 1;
    145 		}
    146 	}
    147 
    148 
    149 	return 0;
    150 }
    151 
    152 static void
    153 lmtemp_attach(device_t parent, device_t self, void *aux)
    154 {
    155 	struct lmtemp_softc *sc = device_private(self);
    156 	struct i2c_attach_args *ia = aux;
    157 	int i;
    158 
    159 	sc->sc_dev = self;
    160 	if (ia->ia_name == NULL) {
    161 		for (i = 0; lmtemptbl[i].lmtemp_type != -1 ; i++)
    162 			if (lmtemptbl[i].lmtemp_type ==
    163 			    device_cfdata(self)->cf_flags)
    164 				break;
    165 	} else {
    166 		/* XXX - add code when adding other direct matches! */
    167 		i = 0;
    168 	}
    169 
    170 	sc->sc_tag = ia->ia_tag;
    171 	sc->sc_address = ia->ia_addr;
    172 
    173 	aprint_naive(": Temperature Sensor\n");
    174 	if (ia->ia_name) {
    175 		aprint_normal(": %s %s Temperature Sensor\n", ia->ia_name,
    176 			lmtemptbl[i].lmtemp_name);
    177 	} else {
    178 		aprint_normal(": %s Temperature Sensor\n",
    179 			lmtemptbl[i].lmtemp_name);
    180 	}
    181 
    182 	sc->sc_lmtemp_decode = lmtemptbl[i].lmtemp_decode;
    183 
    184 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
    185 
    186 	/* Read temperature limit and remember initial value. */
    187 	if (lmtemp_temp_read(sc, LM75_REG_TOS_SET_POINT, &sc->sc_tmax, 1)
    188 	    != 0) {
    189 		iic_release_bus(sc->sc_tag, I2C_F_POLL);
    190 		return;
    191 	}
    192 
    193 	if (i == lmtemp_lm75)
    194 		lmtemp_setup_sysctl(sc);
    195 
    196 	/* Set the configuration of the LM75 to defaults. */
    197 	if (lmtemp_config_write(sc, LM75_CONFIG_FAULT_QUEUE_4) != 0) {
    198 		aprint_error_dev(self, "unable to write config register\n");
    199 		iic_release_bus(sc->sc_tag, I2C_F_POLL);
    200 		return;
    201 	}
    202 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
    203 
    204 	sc->sc_sme = sysmon_envsys_create();
    205 	/* Initialize sensor data. */
    206 	sc->sc_sensor.units =  ENVSYS_STEMP;
    207 	sc->sc_sensor.state =  ENVSYS_SINVALID;
    208 	(void)strlcpy(sc->sc_sensor.desc,
    209 	    ia->ia_name? ia->ia_name : device_xname(self),
    210 	    sizeof(sc->sc_sensor.desc));
    211 	if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) {
    212 		sysmon_envsys_destroy(sc->sc_sme);
    213 		return;
    214 	}
    215 
    216 	/* Hook into system monitor. */
    217 	sc->sc_sme->sme_name = device_xname(self);
    218 	sc->sc_sme->sme_cookie = sc;
    219 	sc->sc_sme->sme_refresh = lmtemp_refresh;
    220 
    221 	if (sysmon_envsys_register(sc->sc_sme)) {
    222 		aprint_error_dev(self, "unable to register with sysmon\n");
    223 		sysmon_envsys_destroy(sc->sc_sme);
    224 	}
    225 }
    226 
    227 static int
    228 lmtemp_config_write(struct lmtemp_softc *sc, uint8_t val)
    229 {
    230 	uint8_t cmdbuf[2];
    231 
    232 	cmdbuf[0] = LM75_REG_CONFIG;
    233 	cmdbuf[1] = val;
    234 
    235 	return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
    236 	    sc->sc_address, cmdbuf, 1, &cmdbuf[1], 1, I2C_F_POLL);
    237 }
    238 
    239 static int
    240 lmtemp_temp_write(struct lmtemp_softc *sc, int reg, uint16_t val)
    241 {
    242 	uint8_t cmdbuf[3];
    243 
    244 	cmdbuf[0] = reg;
    245 	cmdbuf[1] = (val >> 1) & 0xff;
    246 	cmdbuf[2] = (val & 1) << 7;
    247 
    248 	return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
    249 	    sc->sc_address, cmdbuf, 1, &cmdbuf[1], 2, I2C_F_POLL);
    250 }
    251 
    252 static int
    253 lmtemp_temp_read(struct lmtemp_softc *sc, uint8_t which, uint32_t *valp,
    254     int degc)
    255 {
    256 	int error;
    257 	uint8_t cmdbuf[1];
    258 	uint8_t buf[LM75_TEMP_LEN];
    259 
    260 	cmdbuf[0] = which;
    261 
    262 	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
    263 	    sc->sc_address, cmdbuf, 1, buf, LM75_TEMP_LEN, 0);
    264 	if (error)
    265 		return error;
    266 
    267 	*valp = sc->sc_lmtemp_decode(buf, degc);
    268 	return 0;
    269 }
    270 
    271 static void
    272 lmtemp_refresh_sensor_data(struct lmtemp_softc *sc)
    273 {
    274 	uint32_t val;
    275 	int error;
    276 
    277 	error = lmtemp_temp_read(sc, LM75_REG_TEMP, &val, 0);
    278 	if (error) {
    279 #if 0
    280 		aprint_error_dev(sc->sc_dev, "unable to read temperature, error = %d\n",
    281 		    error);
    282 #endif
    283 		sc->sc_sensor.state = ENVSYS_SINVALID;
    284 		return;
    285 	}
    286 
    287 	sc->sc_sensor.value_cur = val;
    288 	sc->sc_sensor.state = ENVSYS_SVALID;
    289 }
    290 
    291 static void
    292 lmtemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
    293 {
    294 	struct lmtemp_softc *sc = sme->sme_cookie;
    295 
    296 	iic_acquire_bus(sc->sc_tag, 0);	/* also locks our instance */
    297 	lmtemp_refresh_sensor_data(sc);
    298 	iic_release_bus(sc->sc_tag, 0);	/* also unlocks our instance */
    299 }
    300 
    301 static uint32_t
    302 lmtemp_decode_lm75(const uint8_t *buf, int degc)
    303 {
    304 	int temp;
    305 	uint32_t val;
    306 
    307 	/*
    308 	 * LM75 temps are the most-significant 9 bits of a 16-bit reg.
    309 	 * sign-extend the MSB and add in the 0.5 from the LSB
    310 	 */
    311 	temp = (int8_t) buf[0];
    312 	temp = (temp << 1) + ((buf[1] >> 7) & 0x1);
    313 
    314 	/* Temp is given in 1/2 deg. C, we convert to C or uK. */
    315 	if (degc)
    316 		val = temp / 2;
    317 	else
    318 		val = temp * 500000 + 273150000;
    319 
    320 	return val;
    321 }
    322 
    323 static uint32_t
    324 lmtemp_decode_ds75(const uint8_t *buf, int degc)
    325 {
    326 	int temp;
    327 
    328 	/*
    329 	 * Sign-extend the MSB byte, and add in the fractions of a
    330 	 * degree contained in the LSB (precision 1/16th DegC).
    331 	 */
    332 	temp = (int8_t)buf[0];
    333 	temp = (temp << 4) | ((buf[1] >> 4) & 0xf);
    334 
    335 	/*
    336 	 * Conversion to C or uK is simple.
    337 	 */
    338 	if (degc)
    339 		return temp / 16;
    340 	else
    341 		return (temp * 62500 + 273150000);
    342 }
    343 
    344 static uint32_t
    345 lmtemp_decode_lm77(const uint8_t *buf, int degc)
    346 {
    347 	int temp;
    348 	uint32_t val;
    349 
    350 	/*
    351 	 * Describe each bits of temperature registers on LM77.
    352 	 *   D15 - D12:	Sign
    353 	 *   D11 - D3 :	Bit8(MSB) - Bit0
    354 	 */
    355 	temp = (int8_t)buf[0];
    356 	temp = (temp << 5) | ((buf[1] >> 3) & 0x1f);
    357 
    358 	/* Temp is given in 1/2 deg. C, we convert to C or uK. */
    359 	if (degc)
    360 		val = temp / 2;
    361 	else
    362 		val = temp * 500000 + 273150000;
    363 
    364 	return val;
    365 }
    366 
    367 static void
    368 lmtemp_setup_sysctl(struct lmtemp_softc *sc)
    369 {
    370 	const struct sysctlnode *me = NULL, *node = NULL;
    371 
    372 	sysctl_createv(NULL, 0, NULL, &me,
    373 	    CTLFLAG_READWRITE,
    374 	    CTLTYPE_NODE, device_xname(sc->sc_dev), NULL,
    375 	    NULL, 0, NULL, 0,
    376 	    CTL_MACHDEP, CTL_CREATE, CTL_EOL);
    377 
    378 	sysctl_createv(NULL, 0, NULL, &node,
    379 	    CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
    380 	    CTLTYPE_INT, "temp", "Threshold temperature",
    381 	    sysctl_lm75_temp, 1, (void *)sc, 0,
    382 	    CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL);
    383 }
    384 
    385 static int
    386 sysctl_lm75_temp(SYSCTLFN_ARGS)
    387 {
    388 	struct sysctlnode node = *rnode;
    389 	struct lmtemp_softc *sc = node.sysctl_data;
    390 	int temp;
    391 
    392 	if (newp) {
    393 
    394 		/* we're asked to write */
    395 		node.sysctl_data = &sc->sc_tmax;
    396 		if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
    397 
    398 			temp = *(int *)node.sysctl_data;
    399 			sc->sc_tmax = temp;
    400 			iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
    401 			lmtemp_temp_write(sc, LM75_REG_THYST_SET_POINT,
    402 			    (sc->sc_tmax - 5) * 2);
    403 			lmtemp_temp_write(sc, LM75_REG_TOS_SET_POINT,
    404 			    sc->sc_tmax * 2);
    405 			iic_release_bus(sc->sc_tag, I2C_F_POLL);
    406 			return 0;
    407 		}
    408 		return EINVAL;
    409 	} else {
    410 
    411 		node.sysctl_data = &sc->sc_tmax;
    412 		node.sysctl_size = 4;
    413 		return (sysctl_lookup(SYSCTLFN_CALL(&node)));
    414 	}
    415 
    416 	return 0;
    417 }
    418 
    419 SYSCTL_SETUP(sysctl_lmtemp_setup, "sysctl lmtemp subtree setup")
    420 {
    421 
    422 	sysctl_createv(NULL, 0, NULL, NULL,
    423 		       CTLFLAG_PERMANENT,
    424 		       CTLTYPE_NODE, "machdep", NULL,
    425 		       NULL, 0, NULL, 0,
    426 		       CTL_MACHDEP, CTL_EOL);
    427 }
    428 
    429 
    430