mcp980x.c revision 1.8
1/*	$NetBSD: mcp980x.c,v 1.8 2024/01/16 21:08:52 andvar Exp $ */
2
3/*-
4 * Copyright (c) 2013 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Radoslaw Kujawa.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * Microchip MCP9800/1/2/3 2-Wire High-Accuracy Temperature Sensor driver.
34 *
35 * TODO: better error checking, particularly in user settable limits.
36 *
37 * Note: MCP9805 is different and is supported by the sdtemp(4) driver.
38 */
39
40#include <sys/cdefs.h>
41__KERNEL_RCSID(0, "$NetBSD: mcp980x.c,v 1.8 2024/01/16 21:08:52 andvar Exp $");
42
43#include <sys/param.h>
44#include <sys/systm.h>
45#include <sys/device.h>
46#include <sys/kernel.h>
47#include <sys/mutex.h>
48#include <sys/endian.h>
49#include <sys/sysctl.h>
50
51#include <sys/bus.h>
52#include <dev/i2c/i2cvar.h>
53
54#include <dev/sysmon/sysmonvar.h>
55
56#include <dev/i2c/mcp980xreg.h>
57
58struct mcp980x_softc {
59	device_t		sc_dev;
60
61	i2c_tag_t		sc_tag;
62	i2c_addr_t		sc_addr;
63
64	int			sc_res;
65	int			sc_hyst;
66	int			sc_limit;
67
68	/* envsys(4) stuff */
69	struct sysmon_envsys	*sc_sme;
70	envsys_data_t		sc_sensor;
71	kmutex_t		sc_lock;
72};
73
74
75static int mcp980x_match(device_t, cfdata_t, void *);
76static void mcp980x_attach(device_t, device_t, void *);
77
78static uint8_t mcp980x_reg_read_1(struct mcp980x_softc *, uint8_t);
79static uint16_t mcp980x_reg_read_2(struct mcp980x_softc *, uint8_t);
80static void mcp980x_reg_write_1(struct mcp980x_softc *, uint8_t, uint8_t);
81
82static uint8_t mcp980x_resolution_get(struct mcp980x_softc *);
83static void mcp980x_resolution_set(struct mcp980x_softc *, uint8_t);
84
85static int8_t mcp980x_hysteresis_get(struct mcp980x_softc *);
86static void mcp980x_hysteresis_set(struct mcp980x_softc *, int8_t);
87static int8_t mcp980x_templimit_get(struct mcp980x_softc *);
88static void mcp980x_templimit_set(struct mcp980x_softc *, int8_t);
89
90static int8_t mcp980x_s8b_get(struct mcp980x_softc *, uint8_t);
91static void mcp980x_s8b_set(struct mcp980x_softc *, uint8_t, int8_t);
92
93static uint32_t mcp980x_temperature(struct mcp980x_softc *);
94
95static void mcp980x_envsys_register(struct mcp980x_softc *);
96static void mcp980x_envsys_refresh(struct sysmon_envsys *, envsys_data_t *);
97
98static void mcp980x_setup_sysctl(struct mcp980x_softc *);
99static int sysctl_mcp980x_res(SYSCTLFN_ARGS);
100static int sysctl_mcp980x_hysteresis(SYSCTLFN_ARGS);
101static int sysctl_mcp980x_templimit(SYSCTLFN_ARGS);
102
103CFATTACH_DECL_NEW(mcp980x, sizeof (struct mcp980x_softc),
104    mcp980x_match, mcp980x_attach, NULL, NULL);
105
106static int
107mcp980x_match(device_t parent, cfdata_t cf, void *aux)
108{
109
110	if (ia->ia_addr >= MCP980X_ADDR_CONST &&
111	    ia->ia_addr <= (MCP980X_ADDR_CONST + MCP980X_ADDR_VAR))
112		return I2C_MATCH_ADDRESS_ONLY;
113
114	return 0;
115}
116
117static void
118mcp980x_attach(device_t parent, device_t self, void *aux)
119{
120	struct mcp980x_softc *sc = device_private(self);
121	struct i2c_attach_args *ia = aux;
122
123	sc->sc_dev = self;
124	sc->sc_addr = ia->ia_addr;
125	sc->sc_tag = ia->ia_tag;
126
127	aprint_normal(": Microchip MCP980x Temperature Sensor\n");
128
129	sc->sc_res = MCP980X_CONFIG_ADC_RES_12BIT;
130	mcp980x_resolution_set(sc, sc->sc_res);
131
132	sc->sc_hyst = mcp980x_hysteresis_get(sc);
133	sc->sc_limit = mcp980x_templimit_get(sc);
134
135	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
136
137	mcp980x_setup_sysctl(sc);
138	mcp980x_envsys_register(sc);
139}
140
141static uint16_t
142mcp980x_reg_read_2(struct mcp980x_softc *sc, uint8_t reg)
143{
144	uint16_t rv;
145
146	if (iic_acquire_bus(sc->sc_tag, 0) != 0) {
147		aprint_error_dev(sc->sc_dev, "cannot acquire bus for read\n");
148		return 0;
149	}
150
151	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, &reg,
152	    1, &rv, 2, 0)) {
153		iic_release_bus(sc->sc_tag, 0);
154		aprint_error_dev(sc->sc_dev, "cannot execute operation\n");
155		return 0;
156	}
157	iic_release_bus(sc->sc_tag, 0);
158
159	return be16toh(rv);
160}
161
162static uint8_t
163mcp980x_reg_read_1(struct mcp980x_softc *sc, uint8_t reg)
164{
165	uint8_t rv;
166
167	if (iic_acquire_bus(sc->sc_tag, 0) != 0) {
168		aprint_error_dev(sc->sc_dev, "cannot acquire bus for read\n");
169		return 0;
170	}
171
172	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, &reg,
173	    1, &rv, 1, 0)) {
174		iic_release_bus(sc->sc_tag, 0);
175		aprint_error_dev(sc->sc_dev, "cannot execute operation\n");
176		return 0;
177	}
178	iic_release_bus(sc->sc_tag, 0);
179
180	return rv;
181}
182
183static void
184mcp980x_reg_write_2(struct mcp980x_softc *sc, uint8_t reg, uint16_t val)
185{
186	uint16_t beval;
187
188	beval = htobe16(val);
189
190        if (iic_acquire_bus(sc->sc_tag, 0) != 0) {
191		aprint_error_dev(sc->sc_dev, "cannot acquire bus for write\n");
192		return;
193	}
194
195        if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, &reg,
196	    1, &beval, 2, 0)) {
197		aprint_error_dev(sc->sc_dev, "cannot execute operation\n");
198        }
199
200	iic_release_bus(sc->sc_tag, 0);
201
202}
203
204static void
205mcp980x_reg_write_1(struct mcp980x_softc *sc, uint8_t reg, uint8_t val)
206{
207        if (iic_acquire_bus(sc->sc_tag, 0) != 0) {
208		aprint_error_dev(sc->sc_dev, "cannot acquire bus for write\n");
209		return;
210	}
211
212        if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, &reg,
213	    1, &val, 1, 0)) {
214		aprint_error_dev(sc->sc_dev, "cannot execute operation\n");
215        }
216
217	iic_release_bus(sc->sc_tag, 0);
218
219}
220
221static int8_t
222mcp980x_templimit_get(struct mcp980x_softc *sc)
223{
224	return mcp980x_s8b_get(sc, MCP980X_TEMP_LIMIT);
225}
226
227static void
228mcp980x_templimit_set(struct mcp980x_softc *sc, int8_t val)
229{
230	mcp980x_s8b_set(sc, MCP980X_TEMP_LIMIT, val);
231}
232
233static int8_t
234mcp980x_hysteresis_get(struct mcp980x_softc *sc)
235{
236	return mcp980x_s8b_get(sc, MCP980X_TEMP_HYSTERESIS);
237}
238
239static void
240mcp980x_hysteresis_set(struct mcp980x_softc *sc, int8_t val)
241{
242	mcp980x_s8b_set(sc, MCP980X_TEMP_HYSTERESIS, val);
243}
244
245static int8_t
246mcp980x_s8b_get(struct mcp980x_softc *sc, uint8_t reg)
247{
248	return mcp980x_reg_read_2(sc, reg) >> MCP980X_TEMP_HYSTLIMIT_INT_SHIFT;
249}
250
251static void
252mcp980x_s8b_set(struct mcp980x_softc *sc, uint8_t reg, int8_t val)
253{
254	mcp980x_reg_write_2(sc, reg, val << MCP980X_TEMP_HYSTLIMIT_INT_SHIFT);
255}
256
257static uint8_t
258mcp980x_resolution_get(struct mcp980x_softc *sc)
259{
260	uint8_t cfg, res;
261
262	cfg = mcp980x_reg_read_1(sc, MCP980X_CONFIG);
263	res = (cfg & MCP980X_CONFIG_ADC_RES) >>
264	    MCP980X_CONFIG_ADC_RES_SHIFT;
265
266	return res;
267}
268
269static void
270mcp980x_resolution_set(struct mcp980x_softc *sc, uint8_t res)
271{
272	uint8_t cfg;
273
274	/* read config register but discard resolution bits */
275	cfg = mcp980x_reg_read_1(sc, MCP980X_CONFIG) & ~MCP980X_CONFIG_ADC_RES;
276	/* set resolution bits to new value */
277	cfg |= res << MCP980X_CONFIG_ADC_RES_SHIFT;
278
279	mcp980x_reg_write_1(sc, MCP980X_CONFIG, cfg);
280}
281
282/* Get temperature in microKelvins. */
283static uint32_t
284mcp980x_temperature(struct mcp980x_softc *sc)
285{
286	uint16_t raw;
287	uint32_t rv, uk, basedegc;
288
289	raw = mcp980x_reg_read_2(sc, MCP980X_AMBIENT_TEMP);
290
291	basedegc = (raw & MCP980X_AMBIENT_TEMP_DEGREES) >>
292	    MCP980X_AMBIENT_TEMP_DEGREES_SHIFT;
293
294	uk = 1000000 * basedegc;
295
296	if (raw & MCP980X_AMBIENT_TEMP_05DEGREE)
297		uk += 500000;
298	if (raw & MCP980X_AMBIENT_TEMP_025DEGREE)
299		uk += 250000;
300	if (raw & MCP980X_AMBIENT_TEMP_0125DEGREE)
301		uk += 125000;
302	if (raw & MCP980X_AMBIENT_TEMP_00625DEGREE)
303		uk += 62500;
304
305	if (raw & MCP980X_AMBIENT_TEMP_SIGN)
306		rv = 273150000U - uk;
307	else
308		rv = 273150000U + uk;
309
310	return rv;
311}
312
313static void
314mcp980x_envsys_register(struct mcp980x_softc *sc)
315{
316	sc->sc_sme = sysmon_envsys_create();
317
318	strlcpy(sc->sc_sensor.desc, "Ambient temp",
319	    sizeof(sc->sc_sensor.desc));
320	sc->sc_sensor.units = ENVSYS_STEMP;
321	sc->sc_sensor.state = ENVSYS_SINVALID;
322
323	if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) {
324		aprint_error_dev(sc->sc_dev,
325		    "error attaching sensor\n");
326		return;
327	}
328
329	sc->sc_sme->sme_name = device_xname(sc->sc_dev);
330	sc->sc_sme->sme_cookie = sc;
331	sc->sc_sme->sme_refresh = mcp980x_envsys_refresh;
332
333	if (sysmon_envsys_register(sc->sc_sme)) {
334		aprint_error_dev(sc->sc_dev, "unable to register in sysmon\n");
335		sysmon_envsys_destroy(sc->sc_sme);
336	}
337}
338
339static void
340mcp980x_envsys_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
341{
342	struct mcp980x_softc *sc = sme->sme_cookie;
343
344	mutex_enter(&sc->sc_lock);
345
346	edata->value_cur = mcp980x_temperature(sc);
347	edata->state = ENVSYS_SVALID;
348
349	mutex_exit(&sc->sc_lock);
350}
351
352static void
353mcp980x_setup_sysctl(struct mcp980x_softc *sc)
354{
355	const struct sysctlnode *me = NULL, *node = NULL;
356
357	sysctl_createv(NULL, 0, NULL, &me,
358	    CTLFLAG_READWRITE,
359	    CTLTYPE_NODE, device_xname(sc->sc_dev), NULL,
360	    NULL, 0, NULL, 0,
361	    CTL_MACHDEP, CTL_CREATE, CTL_EOL);
362
363	sysctl_createv(NULL, 0, NULL, &node,
364	    CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
365	    CTLTYPE_INT, "res", "Resolution",
366	    sysctl_mcp980x_res, 1, (void *)sc, 0,
367	    CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL);
368
369	sysctl_createv(NULL, 0, NULL, &node,
370	    CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
371	    CTLTYPE_INT, "hysteresis", "Temperature hysteresis",
372	    sysctl_mcp980x_hysteresis, 1, (void *)sc, 0,
373	    CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL);
374
375	sysctl_createv(NULL, 0, NULL, &node,
376	    CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
377	    CTLTYPE_INT, "templimit", "Temperature limit",
378	    sysctl_mcp980x_templimit, 1, (void *)sc, 0,
379	    CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL);
380}
381
382
383SYSCTL_SETUP(sysctl_mcp980x_setup, "sysctl mcp980x subtree setup")
384{
385	sysctl_createv(NULL, 0, NULL, NULL, CTLFLAG_PERMANENT,
386	    CTLTYPE_NODE, "machdep", NULL, NULL, 0, NULL, 0,
387	    CTL_MACHDEP, CTL_EOL);
388}
389
390
391static int
392sysctl_mcp980x_res(SYSCTLFN_ARGS)
393{
394	struct sysctlnode node = *rnode;
395	struct mcp980x_softc *sc = node.sysctl_data;
396	int newres, err;
397
398	node.sysctl_data = &sc->sc_res;
399	if ((err = (sysctl_lookup(SYSCTLFN_CALL(&node)))) != 0)
400		return err;
401
402	if (newp) {
403		newres = *(int *)node.sysctl_data;
404		if (newres > MCP980X_CONFIG_ADC_RES_12BIT)
405			return EINVAL;
406		sc->sc_res = (uint8_t) newres;
407		mcp980x_resolution_set(sc, sc->sc_res);
408		return 0;
409	} else {
410		sc->sc_res = mcp980x_resolution_get(sc);
411		node.sysctl_size = 4;
412	}
413
414	return err;
415}
416
417static int
418sysctl_mcp980x_hysteresis(SYSCTLFN_ARGS)
419{
420	struct sysctlnode node = *rnode;
421	struct mcp980x_softc *sc = node.sysctl_data;
422	int newhyst, err;
423
424	node.sysctl_data = &sc->sc_hyst;
425	if ((err = (sysctl_lookup(SYSCTLFN_CALL(&node)))) != 0)
426		return err;
427
428	if (newp) {
429		newhyst = *(int *)node.sysctl_data;
430		sc->sc_hyst = newhyst;
431		mcp980x_hysteresis_set(sc, sc->sc_hyst);
432		return 0;
433	} else {
434		sc->sc_hyst = mcp980x_hysteresis_get(sc);
435		node.sysctl_size = 4;
436	}
437
438	return err;
439}
440
441static int
442sysctl_mcp980x_templimit(SYSCTLFN_ARGS)
443{
444	struct sysctlnode node = *rnode;
445	struct mcp980x_softc *sc = node.sysctl_data;
446	int newlimit, err;
447
448	node.sysctl_data = &sc->sc_limit;
449	if ((err = (sysctl_lookup(SYSCTLFN_CALL(&node)))) != 0)
450		return err;
451
452	if (newp) {
453		newlimit = *(int *)node.sysctl_data;
454		sc->sc_limit = newlimit;
455		mcp980x_templimit_set(sc, sc->sc_limit);
456		return 0;
457	} else {
458		sc->sc_limit = mcp980x_templimit_get(sc);
459		node.sysctl_size = 4;
460	}
461
462	return err;
463}
464
465