1219131Srwatson/*	$OpenBSD: lm75.c,v 1.21 2022/04/06 18:59:28 naddy Exp $	*/
2219131Srwatson/*	$NetBSD: lm75.c,v 1.1 2003/09/30 00:35:31 thorpej Exp $	*/
3219131Srwatson/*
4219131Srwatson * Copyright (c) 2006 Theo de Raadt <deraadt@openbsd.org>
5219131Srwatson * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
6219131Srwatson *
7219131Srwatson * Permission to use, copy, modify, and distribute this software for any
8219131Srwatson * purpose with or without fee is hereby granted, provided that the above
9219131Srwatson * copyright notice and this permission notice appear in all copies.
10219131Srwatson *
11219131Srwatson * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12219131Srwatson * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13219131Srwatson * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14219131Srwatson * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15219131Srwatson * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16219131Srwatson * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17219131Srwatson * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18219131Srwatson */
19219131Srwatson
20219131Srwatson/*
21219131Srwatson * National Semiconductor LM75/LM76/LM77 temperature sensor.
22219131Srwatson */
23219131Srwatson
24219131Srwatson#include <sys/param.h>
25219131Srwatson#include <sys/systm.h>
26219131Srwatson#include <sys/device.h>
27219131Srwatson#include <sys/sensors.h>
28219131Srwatson
29219131Srwatson#include <dev/i2c/i2cvar.h>
30219131Srwatson
31219131Srwatson#define	LM_MODEL_LM75	1
32219131Srwatson#define	LM_MODEL_LM77	2
33219131Srwatson#define	LM_MODEL_DS1775	3
34219131Srwatson#define	LM_MODEL_LM75A	4
35219131Srwatson#define	LM_MODEL_LM76	5
36219131Srwatson
37219131Srwatson#define LM_POLLTIME	3	/* 3s */
38219131Srwatson
39219131Srwatson#define	LM75_REG_TEMP			0x00
40219131Srwatson#define	LM75_REG_CONFIG			0x01
41219131Srwatson#define  LM75_CONFIG_SHUTDOWN		0x01
42219131Srwatson#define  LM75_CONFIG_CMPINT		0x02
43219131Srwatson#define  LM75_CONFIG_OSPOLARITY		0x04
44219131Srwatson#define  LM75_CONFIG_FAULT_QUEUE_MASK	0x18
45219131Srwatson#define  LM75_CONFIG_FAULT_QUEUE_1	(0 << 3)
46219131Srwatson#define  LM75_CONFIG_FAULT_QUEUE_2	(1 << 3)
47219131Srwatson#define  LM75_CONFIG_FAULT_QUEUE_4	(2 << 3)
48219131Srwatson#define  LM75_CONFIG_FAULT_QUEUE_6	(3 << 3)
49219131Srwatson#define  LM77_CONFIG_INTPOLARITY	0x08
50219131Srwatson#define  LM77_CONFIG_FAULT_QUEUE_4	0x10
51219131Srwatson#define  DS1755_CONFIG_RESOLUTION(i)	(9 + (((i) >> 5) & 3))
52219131Srwatson#define	LM75_REG_THYST_SET_POINT	0x02
53219131Srwatson#define	LM75_REG_TOS_SET_POINT		0x03
54219131Srwatson#define	LM77_REG_TLOW			0x04
55219131Srwatson#define	LM77_REG_THIGH			0x05
56219131Srwatson
57219131Srwatsonstruct lmtemp_softc {
58219131Srwatson	struct device sc_dev;
59219131Srwatson	i2c_tag_t sc_tag;
60219131Srwatson	int	sc_addr;
61219131Srwatson	int	sc_model;
62219131Srwatson	int	sc_bits;
63219131Srwatson	int	sc_ratio;
64219131Srwatson
65219131Srwatson	struct ksensor sc_sensor;
66219131Srwatson	struct ksensordev sc_sensordev;
67219131Srwatson};
68219131Srwatson
69219131Srwatsonint  lmtemp_match(struct device *, void *, void *);
70219131Srwatsonvoid lmtemp_attach(struct device *, struct device *, void *);
71219131Srwatson
72219131Srwatsonconst struct cfattach lmtemp_ca = {
73219131Srwatson	sizeof(struct lmtemp_softc),
74219131Srwatson	lmtemp_match,
75219131Srwatson	lmtemp_attach
76219131Srwatson};
77219131Srwatson
78219131Srwatsonstruct cfdriver lmtemp_cd = {
79219131Srwatson	NULL, "lmtemp", DV_DULL
80219131Srwatson};
81219131Srwatson
82219131Srwatson/*
83219131Srwatson * Temperature on the LM75 is represented by a 9-bit two's complement
84219131Srwatson * integer in steps of 0.5C.  The following examples are taken from
85219131Srwatson * the LM75 data sheet:
86219131Srwatson *
87219131Srwatson *	+125C	0 1111 1010	0x0fa
88219131Srwatson *	+25C	0 0011 0010	0x032
89219131Srwatson *	+0.5C	0 0000 0001	0x001
90219131Srwatson *	0C	0 0000 0000	0x000
91219131Srwatson *	-0.5C	1 1111 1111	0x1ff
92219131Srwatson *	-25C	1 1100 1110	0x1ce
93219131Srwatson *	-55C	1 1001 0010	0x192
94219131Srwatson *
95219131Srwatson * Temperature on the LM75A is represented by an 11-bit two's complement
96219131Srwatson * integer in steps of 0.125C.  The LM75A can be treated like an LM75 if
97219131Srwatson * the extra precision is not required.  The following examples are
98219131Srwatson * taken from the LM75A data sheet:
99219131Srwatson *
100219131Srwatson *	+127.000C	011 1111 1000	0x3f8
101219131Srwatson *	+126.875C	011 1111 0111	0x3f7
102219131Srwatson *	+126.125C	011 1111 0001	0x3f1
103219131Srwatson *	+125.000C	011 1110 1000	0x3e8
104219131Srwatson *	+25.000C	000 1100 1000	0x0c8
105219131Srwatson *	+0.125C		000 0000 0001	0x001
106219131Srwatson *	0C		000 0000 0000	0x000
107219131Srwatson *	-0.125C		111 1111 1111	0x7ff
108219131Srwatson *	-25.000C	111 0011 1000	0x738
109219131Srwatson *	-54.875C	110 0100 1001	0x649
110219131Srwatson *	-55.000C	110 0100 1000	0x648
111219131Srwatson *
112219131Srwatson * Temperature on the LM77 is represented by a 13-bit two's complement
113219131Srwatson * integer in steps of 0.5C.  The LM76 is similar, but the integer is
114219131Srwatson * in steps of 0.065C
115219131Srwatson *
116219131Srwatson * LM75 temperature word:
117219131Srwatson *
118219131Srwatson * MSB Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 X X X X X X X
119219131Srwatson * 15  14   13   12   11   10   9    8    7    6 5 4 3 2 1 0
120219131Srwatson *
121219131Srwatson *
122219131Srwatson * LM75A temperature word:
123219131Srwatson *
124219131Srwatson * MSB Bit9 Bit8 Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 X X X X X
125219131Srwatson * 15  14   13   12   11   10   9    8    7    6    5    4 3 2 1 0
126219131Srwatson *
127219131Srwatson *
128219131Srwatson * LM77 temperature word:
129219131Srwatson *
130219131Srwatson * Sign Sign Sign Sign MSB Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 Status bits
131219131Srwatson * 15   14   13   12   11  10   9    8    7    6    5    4    3    2 1 0
132219131Srwatson */
133219131Srwatson
134219131Srwatsonint  lmtemp_temp_read(struct lmtemp_softc *, uint8_t, int *);
135219131Srwatsonvoid lmtemp_refresh_sensor_data(void *);
136219131Srwatson
137219131Srwatsonint
138219131Srwatsonlmtemp_match(struct device *parent, void *match, void *aux)
139219131Srwatson{
140219131Srwatson	struct i2c_attach_args *ia = aux;
141219131Srwatson
142219131Srwatson	if (strcmp(ia->ia_name, "lm75") == 0 ||
143219131Srwatson	    strcmp(ia->ia_name, "lm76") == 0 ||
144219131Srwatson	    strcmp(ia->ia_name, "lm77") == 0 ||
145219131Srwatson	    strcmp(ia->ia_name, "ds1775") == 0 ||
146219131Srwatson	    strcmp(ia->ia_name, "lm75a") == 0)
147219131Srwatson		return (1);
148219131Srwatson	return (0);
149219131Srwatson}
150219131Srwatson
151219131Srwatsonvoid
152219131Srwatsonlmtemp_attach(struct device *parent, struct device *self, void *aux)
153219131Srwatson{
154219131Srwatson	struct lmtemp_softc *sc = (struct lmtemp_softc *)self;
155219131Srwatson	struct i2c_attach_args *ia = aux;
156219131Srwatson	u_int8_t cmd, data;
157219131Srwatson
158219131Srwatson	sc->sc_tag = ia->ia_tag;
159219131Srwatson	sc->sc_addr = ia->ia_addr;
160219131Srwatson
161219131Srwatson	printf(": %s", ia->ia_name);
162219131Srwatson
163219131Srwatson	/* If in SHUTDOWN mode, wake it up */
164219131Srwatson	iic_acquire_bus(sc->sc_tag, 0);
165219131Srwatson	cmd = LM75_REG_CONFIG;
166219131Srwatson	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
167219131Srwatson	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
168219131Srwatson		iic_release_bus(sc->sc_tag, 0);
169219131Srwatson		printf(", fails to respond\n");
170219131Srwatson		return;
171219131Srwatson	}
172219131Srwatson	if (data & LM75_CONFIG_SHUTDOWN) {
173219131Srwatson		data &= ~LM75_CONFIG_SHUTDOWN;
174219131Srwatson		if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
175219131Srwatson		    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
176219131Srwatson			printf(", cannot wake up\n");
177219131Srwatson			iic_release_bus(sc->sc_tag, 0);
178219131Srwatson			return;
179219131Srwatson		}
180219131Srwatson		printf(", woken up");
181219131Srwatson	}
182219131Srwatson	iic_release_bus(sc->sc_tag, 0);
183219131Srwatson
184219131Srwatson	sc->sc_model = LM_MODEL_LM75;
185219131Srwatson	sc->sc_bits = 9;
186219131Srwatson	sc->sc_ratio = 500000;		/* 0.5 degC for LSB */
187219131Srwatson	if (strcmp(ia->ia_name, "lm77") == 0) {
188219131Srwatson		sc->sc_model = LM_MODEL_LM77;
189219131Srwatson		sc->sc_bits = 13;
190219131Srwatson	} else if (strcmp(ia->ia_name, "lm76") == 0) {
191219131Srwatson		sc->sc_model = LM_MODEL_LM76;
192219131Srwatson		sc->sc_bits = 13;
193219131Srwatson		sc->sc_ratio = 62500;	/* 0.0625 degC for LSB */
194219131Srwatson	} else if (strcmp(ia->ia_name, "ds1775") == 0) {
195219131Srwatson		sc->sc_model = LM_MODEL_DS1775;
196219131Srwatson		//sc->sc_bits = DS1755_CONFIG_RESOLUTION(data);
197219131Srwatson	} else if (strcmp(ia->ia_name, "lm75a") == 0) {
198219131Srwatson		/* For simplicity's sake, treat the LM75A as an LM75 */
199219131Srwatson		sc->sc_model = LM_MODEL_LM75A;
200219131Srwatson	}
201219131Srwatson
202219131Srwatson	printf("\n");
203219131Srwatson
204219131Srwatson	/* Initialize sensor data */
205219131Srwatson	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
206219131Srwatson	    sizeof(sc->sc_sensordev.xname));
207219131Srwatson	sc->sc_sensor.type = SENSOR_TEMP;
208219131Srwatson
209219131Srwatson	/* Hook into the hw.sensors sysctl */
210219131Srwatson	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
211219131Srwatson	sensordev_install(&sc->sc_sensordev);
212219131Srwatson
213219131Srwatson	sensor_task_register(sc, lmtemp_refresh_sensor_data, LM_POLLTIME);
214219131Srwatson}
215219131Srwatson
216219131Srwatsonint
217219131Srwatsonlmtemp_temp_read(struct lmtemp_softc *sc, uint8_t which, int *valp)
218219131Srwatson{
219219131Srwatson	u_int8_t cmd = which;
220219131Srwatson	u_int16_t data = 0x0000;
221219131Srwatson	int error;
222219131Srwatson
223219131Srwatson	iic_acquire_bus(sc->sc_tag, 0);
224219131Srwatson	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
225219131Srwatson	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0);
226219131Srwatson	iic_release_bus(sc->sc_tag, 0);
227219131Srwatson	if (error)
228219131Srwatson		return (error);
229219131Srwatson
230219131Srwatson	/* Some chips return transient 0's.. we try next time */
231219131Srwatson	if (data == 0x0000)
232219131Srwatson		return (1);
233219131Srwatson
234219131Srwatson	/* convert to half-degrees C */
235219131Srwatson	*valp = betoh16(data) / (1 << (16 - sc->sc_bits));
236219131Srwatson	return (0);
237219131Srwatson}
238219131Srwatson
239219131Srwatsonvoid
240219131Srwatsonlmtemp_refresh_sensor_data(void *aux)
241219131Srwatson{
242224812Sjonathan	struct lmtemp_softc *sc = aux;
243219131Srwatson	int val;
244219131Srwatson	int error;
245219131Srwatson
246219131Srwatson	error = lmtemp_temp_read(sc, LM75_REG_TEMP, &val);
247219131Srwatson	if (error) {
248219131Srwatson#if 0
249219131Srwatson		printf("%s: unable to read temperature, error = %d\n",
250219131Srwatson		    sc->sc_dev.dv_xname, error);
251219131Srwatson#endif
252219131Srwatson		sc->sc_sensor.flags |= SENSOR_FINVALID;
253219131Srwatson		return;
254219131Srwatson	}
255219131Srwatson
256219131Srwatson	sc->sc_sensor.value = val * sc->sc_ratio + 273150000;
257219131Srwatson	sc->sc_sensor.flags &= ~SENSOR_FINVALID;
258219131Srwatson}
259219131Srwatson