mcp980x.c revision 1.2
1/*	$NetBSD: mcp980x.c,v 1.2 2013/10/15 10:18:49 rkujawa 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 * Note: MCP9805 is different and is supported by the sdtemp(4) driver.
36 */
37
38#include <sys/cdefs.h>
39__KERNEL_RCSID(0, "$NetBSD: mcp980x.c,v 1.2 2013/10/15 10:18:49 rkujawa 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/mutex.h>
46#include <sys/endian.h>
47#include <sys/sysctl.h>
48
49#include <sys/bus.h>
50#include <dev/i2c/i2cvar.h>
51
52#include <dev/sysmon/sysmonvar.h>
53
54#include <dev/i2c/mcp980xreg.h>
55
56struct mcp980x_softc {
57	device_t		sc_dev;
58
59	i2c_tag_t		sc_tag;
60	i2c_addr_t		sc_addr;
61
62	int			sc_res;
63
64	/* envsys(4) stuff */
65	struct sysmon_envsys	*sc_sme;
66	envsys_data_t		sc_sensor;
67	kmutex_t		sc_lock;
68};
69
70
71static int mcp980x_match(device_t, cfdata_t, void *);
72static void mcp980x_attach(device_t, device_t, void *);
73
74static uint8_t mcp980x_reg_read_1(struct mcp980x_softc *, uint8_t);
75static uint16_t mcp980x_reg_read_2(struct mcp980x_softc *, uint8_t);
76static void mcp980x_reg_write_1(struct mcp980x_softc *, uint8_t, uint8_t);
77
78static uint8_t mcp980x_resolution_get(struct mcp980x_softc *);
79static void mcp980x_resolution_set(struct mcp980x_softc *, uint8_t);
80
81static uint32_t mcp980x_temperature(struct mcp980x_softc *);
82
83static void mcp980x_envsys_register(struct mcp980x_softc *);
84static void mcp980x_envsys_refresh(struct sysmon_envsys *, envsys_data_t *);
85
86static void mcp980x_setup_sysctl(struct mcp980x_softc *);
87static int sysctl_mcp980x_res(SYSCTLFN_ARGS);
88
89CFATTACH_DECL_NEW(mcp980x, sizeof (struct mcp980x_softc),
90    mcp980x_match, mcp980x_attach, NULL, NULL);
91
92static int
93mcp980x_match(device_t parent, cfdata_t cf, void *aux)
94{
95	/*
96	 * No sane way to probe? Perhaps at least try to match constant part
97	 * of the I2Caddress.
98	 */
99
100	return 1;
101}
102
103static void
104mcp980x_attach(device_t parent, device_t self, void *aux)
105{
106	struct mcp980x_softc *sc = device_private(self);
107	struct i2c_attach_args *ia = aux;
108
109	sc->sc_dev = self;
110	sc->sc_addr = ia->ia_addr;
111	sc->sc_tag = ia->ia_tag;
112
113	aprint_normal(": Microchip MCP980x Temperature Sensor\n");
114
115	sc->sc_res = MCP980X_CONFIG_ADC_RES_12BIT;
116	mcp980x_resolution_set(sc, sc->sc_res);
117
118	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
119
120	mcp980x_setup_sysctl(sc);
121	mcp980x_envsys_register(sc);
122}
123
124static uint16_t
125mcp980x_reg_read_2(struct mcp980x_softc *sc, uint8_t reg)
126{
127	uint16_t rv;
128
129	if (iic_acquire_bus(sc->sc_tag, I2C_F_POLL) != 0) {
130		aprint_error_dev(sc->sc_dev, "cannot acquire bus for read\n");
131		return 0;
132	}
133
134	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, &reg,
135	    1, &rv, 2, I2C_F_POLL)) {
136		aprint_error_dev(sc->sc_dev, "cannot execute operation\n");
137		iic_release_bus(sc->sc_tag, I2C_F_POLL);
138		return 0;
139	}
140	iic_release_bus(sc->sc_tag, I2C_F_POLL);
141
142	return be16toh(rv);
143}
144
145static uint8_t
146mcp980x_reg_read_1(struct mcp980x_softc *sc, uint8_t reg)
147{
148	uint8_t rv;
149
150	if (iic_acquire_bus(sc->sc_tag, I2C_F_POLL) != 0) {
151		aprint_error_dev(sc->sc_dev, "cannot acquire bus for read\n");
152		return 0;
153	}
154
155	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, &reg,
156	    1, &rv, 1, I2C_F_POLL)) {
157		aprint_error_dev(sc->sc_dev, "cannot execute operation\n");
158		iic_release_bus(sc->sc_tag, I2C_F_POLL);
159		return 0;
160	}
161	iic_release_bus(sc->sc_tag, I2C_F_POLL);
162
163	return rv;
164}
165
166static void
167mcp980x_reg_write_1(struct mcp980x_softc *sc, uint8_t reg, uint8_t val)
168{
169        if (iic_acquire_bus(sc->sc_tag, I2C_F_POLL) != 0) {
170		aprint_error_dev(sc->sc_dev, "cannot acquire bus for write\n");
171		return;
172	}
173
174        if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, &reg,
175	    1, &val, 1, I2C_F_POLL)) {
176		aprint_error_dev(sc->sc_dev, "cannot execute operation\n");
177        }
178
179	iic_release_bus(sc->sc_tag, I2C_F_POLL);
180
181}
182
183static uint8_t
184mcp980x_resolution_get(struct mcp980x_softc *sc)
185{
186	uint8_t cfg, res;
187
188	cfg = mcp980x_reg_read_1(sc, MCP980X_CONFIG);
189	res = (cfg & MCP980X_CONFIG_ADC_RES) >>
190	    MCP980X_CONFIG_ADC_RES_SHIFT;
191
192	return res;
193}
194
195static void
196mcp980x_resolution_set(struct mcp980x_softc *sc, uint8_t res)
197{
198	uint8_t cfg;
199
200	/* read config register but discard resolution bits */
201	cfg = mcp980x_reg_read_1(sc, MCP980X_CONFIG) & ~MCP980X_CONFIG_ADC_RES;
202	/* set resolution bits to new value */
203	cfg |= res << MCP980X_CONFIG_ADC_RES_SHIFT;
204
205	mcp980x_reg_write_1(sc, MCP980X_CONFIG, cfg);
206}
207
208/* Get temperature in microKelvins. */
209static uint32_t
210mcp980x_temperature(struct mcp980x_softc *sc)
211{
212	uint16_t raw;
213	uint32_t rv, uk, basedegc;
214
215	raw = mcp980x_reg_read_2(sc, MCP980X_AMBIENT_TEMP);
216
217	basedegc = (raw & MCP980X_AMBIENT_TEMP_DEGREES) >>
218	    MCP980X_AMBIENT_TEMP_DEGREES_SHIFT;
219
220	uk = 1000000 * basedegc;
221
222	if (raw & MCP980X_AMBIENT_TEMP_05DEGREE)
223		uk += 500000;
224	if (raw & MCP980X_AMBIENT_TEMP_025DEGREE)
225		uk += 250000;
226	if (raw & MCP980X_AMBIENT_TEMP_0125DEGREE)
227		uk += 125000;
228	if (raw & MCP980X_AMBIENT_TEMP_00625DEGREE)
229		uk += 62500;
230
231	if (raw & MCP980X_AMBIENT_TEMP_SIGN)
232		rv = 273150000U - uk;
233	else
234		rv = 273150000U + uk;
235
236	return rv;
237}
238
239static void
240mcp980x_envsys_register(struct mcp980x_softc *sc)
241{
242	sc->sc_sme = sysmon_envsys_create();
243
244	strlcpy(sc->sc_sensor.desc, "Ambient temp",
245	    sizeof(sc->sc_sensor.desc));
246	sc->sc_sensor.units = ENVSYS_STEMP;
247	sc->sc_sensor.state = ENVSYS_SINVALID;
248
249	if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) {
250		aprint_error_dev(sc->sc_dev,
251		    "error attaching sensor\n");
252		return;
253	}
254
255	sc->sc_sme->sme_name = device_xname(sc->sc_dev);
256	sc->sc_sme->sme_cookie = sc;
257	sc->sc_sme->sme_refresh = mcp980x_envsys_refresh;
258
259	if (sysmon_envsys_register(sc->sc_sme)) {
260		aprint_error_dev(sc->sc_dev, "unable to register in sysmon\n");
261		sysmon_envsys_destroy(sc->sc_sme);
262	}
263}
264
265static void
266mcp980x_envsys_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
267{
268	struct mcp980x_softc *sc = sme->sme_cookie;
269
270	mutex_enter(&sc->sc_lock);
271
272	edata->value_cur = mcp980x_temperature(sc);
273	edata->state = ENVSYS_SVALID;
274
275	mutex_exit(&sc->sc_lock);
276}
277
278static void
279mcp980x_setup_sysctl(struct mcp980x_softc *sc)
280{
281	const struct sysctlnode *me = NULL, *node = NULL;
282
283	sysctl_createv(NULL, 0, NULL, &me,
284	    CTLFLAG_READWRITE,
285	    CTLTYPE_NODE, device_xname(sc->sc_dev), NULL,
286	    NULL, 0, NULL, 0,
287	    CTL_MACHDEP, CTL_CREATE, CTL_EOL);
288
289	sysctl_createv(NULL, 0, NULL, &node,
290	    CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
291	    CTLTYPE_INT, "res", "Resolution",
292	    sysctl_mcp980x_res, 1, (void *)sc, 0,
293	    CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL);
294
295}
296
297
298SYSCTL_SETUP(sysctl_lmtemp_setup, "sysctl mcp980x subtree setup")
299{
300	sysctl_createv(NULL, 0, NULL, NULL, CTLFLAG_PERMANENT,
301	    CTLTYPE_NODE, "machdep", NULL, NULL, 0, NULL, 0,
302	    CTL_MACHDEP, CTL_EOL);
303}
304
305
306static int
307sysctl_mcp980x_res(SYSCTLFN_ARGS)
308{
309	struct sysctlnode node = *rnode;
310	struct mcp980x_softc *sc = node.sysctl_data;
311	int newres;
312
313	if (newp) {
314		node.sysctl_data = &sc->sc_res;
315		if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
316			newres = *(int *)node.sysctl_data;
317			if (newres > MCP980X_CONFIG_ADC_RES_12BIT)
318				return EINVAL;
319			sc->sc_res = (uint8_t) newres;
320			mcp980x_resolution_set(sc, sc->sc_res);
321			return 0;
322		}
323	} else {
324		sc->sc_res = mcp980x_resolution_get(sc);
325		node.sysctl_data = &sc->sc_res;
326		node.sysctl_size = 4;
327		return (sysctl_lookup(SYSCTLFN_CALL(&node)));
328	}
329
330	return EINVAL;
331}
332
333