xref: /freebsd/sys/dev/iicbus/adc/ad7418.c (revision 06589d6e029c6ff64a7816d743e0a508abe6193b)
1*06589d6eSEmmanuel Vadot /*-
2*06589d6eSEmmanuel Vadot  * SPDX-License-Identifier: BSD-2-Clause
3*06589d6eSEmmanuel Vadot  *
4*06589d6eSEmmanuel Vadot  * Copyright (c) 2006 Sam Leffler.  All rights reserved.
5*06589d6eSEmmanuel Vadot  *
6*06589d6eSEmmanuel Vadot  * Redistribution and use in source and binary forms, with or without
7*06589d6eSEmmanuel Vadot  * modification, are permitted provided that the following conditions
8*06589d6eSEmmanuel Vadot  * are met:
9*06589d6eSEmmanuel Vadot  * 1. Redistributions of source code must retain the above copyright
10*06589d6eSEmmanuel Vadot  *    notice, this list of conditions and the following disclaimer.
11*06589d6eSEmmanuel Vadot  * 2. Redistributions in binary form must reproduce the above copyright
12*06589d6eSEmmanuel Vadot  *    notice, this list of conditions and the following disclaimer in the
13*06589d6eSEmmanuel Vadot  *    documentation and/or other materials provided with the distribution.
14*06589d6eSEmmanuel Vadot  *
15*06589d6eSEmmanuel Vadot  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16*06589d6eSEmmanuel Vadot  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17*06589d6eSEmmanuel Vadot  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18*06589d6eSEmmanuel Vadot  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19*06589d6eSEmmanuel Vadot  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20*06589d6eSEmmanuel Vadot  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21*06589d6eSEmmanuel Vadot  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22*06589d6eSEmmanuel Vadot  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23*06589d6eSEmmanuel Vadot  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24*06589d6eSEmmanuel Vadot  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25*06589d6eSEmmanuel Vadot  */
26*06589d6eSEmmanuel Vadot 
27*06589d6eSEmmanuel Vadot #include <sys/cdefs.h>
28*06589d6eSEmmanuel Vadot /*
29*06589d6eSEmmanuel Vadot  * Analog Devices AD7418 chip sitting on the I2C bus.
30*06589d6eSEmmanuel Vadot  */
31*06589d6eSEmmanuel Vadot #include <sys/param.h>
32*06589d6eSEmmanuel Vadot #include <sys/systm.h>
33*06589d6eSEmmanuel Vadot #include <sys/kernel.h>
34*06589d6eSEmmanuel Vadot #include <sys/lock.h>
35*06589d6eSEmmanuel Vadot #include <sys/module.h>
36*06589d6eSEmmanuel Vadot #include <sys/bus.h>
37*06589d6eSEmmanuel Vadot #include <sys/sysctl.h>
38*06589d6eSEmmanuel Vadot #include <sys/sx.h>
39*06589d6eSEmmanuel Vadot 
40*06589d6eSEmmanuel Vadot #include <dev/iicbus/iiconf.h>
41*06589d6eSEmmanuel Vadot 
42*06589d6eSEmmanuel Vadot #include "iicbus_if.h"
43*06589d6eSEmmanuel Vadot 
44*06589d6eSEmmanuel Vadot #define	IIC_M_WR	0	/* write operation */
45*06589d6eSEmmanuel Vadot 
46*06589d6eSEmmanuel Vadot #define	AD7418_ADDR	0x50	/* slave address */
47*06589d6eSEmmanuel Vadot 
48*06589d6eSEmmanuel Vadot #define	AD7418_TEMP	0	/* Temperature Value (r/o) */
49*06589d6eSEmmanuel Vadot #define	AD7418_CONF	1	/* Config Register (r/w) */
50*06589d6eSEmmanuel Vadot #define	AD7418_CONF_SHUTDOWN	0x01
51*06589d6eSEmmanuel Vadot #define	AD7418_CONF_CHAN	0xe0	/* channel select mask */
52*06589d6eSEmmanuel Vadot #define	AD7418_CHAN_TEMP	0x00	/* temperature channel */
53*06589d6eSEmmanuel Vadot #define	AD7418_CHAN_VOLT	0x80	/* voltage channel */
54*06589d6eSEmmanuel Vadot #define	AD7418_THYST	2	/* Thyst Setpoint (r/o) */
55*06589d6eSEmmanuel Vadot #define	AD7418_TOTI	3	/* Toti Setpoint */
56*06589d6eSEmmanuel Vadot #define	AD7418_VOLT	4	/* ADC aka Voltage (r/o) */
57*06589d6eSEmmanuel Vadot #define	AD7418_CONF2	5	/* Config2 Register (r/w) */
58*06589d6eSEmmanuel Vadot 
59*06589d6eSEmmanuel Vadot struct ad7418_softc {
60*06589d6eSEmmanuel Vadot 	device_t	sc_dev;
61*06589d6eSEmmanuel Vadot 	struct sx	sc_lock;
62*06589d6eSEmmanuel Vadot 	int		sc_curchan;	/* current channel */
63*06589d6eSEmmanuel Vadot 	int		sc_curtemp;
64*06589d6eSEmmanuel Vadot 	int		sc_curvolt;
65*06589d6eSEmmanuel Vadot 	int		sc_lastupdate;	/* in ticks */
66*06589d6eSEmmanuel Vadot };
67*06589d6eSEmmanuel Vadot 
68*06589d6eSEmmanuel Vadot static void ad7418_update(struct ad7418_softc *);
69*06589d6eSEmmanuel Vadot static int ad7418_read_1(device_t dev, int reg);
70*06589d6eSEmmanuel Vadot static int ad7418_write_1(device_t dev, int reg, int v);
71*06589d6eSEmmanuel Vadot 
72*06589d6eSEmmanuel Vadot static int
ad7418_probe(device_t dev)73*06589d6eSEmmanuel Vadot ad7418_probe(device_t dev)
74*06589d6eSEmmanuel Vadot {
75*06589d6eSEmmanuel Vadot 	/* XXX really probe? */
76*06589d6eSEmmanuel Vadot 	device_set_desc(dev, "Analog Devices AD7418 ADC");
77*06589d6eSEmmanuel Vadot 	return (BUS_PROBE_NOWILDCARD);
78*06589d6eSEmmanuel Vadot }
79*06589d6eSEmmanuel Vadot 
80*06589d6eSEmmanuel Vadot static int
ad7418_sysctl_temp(SYSCTL_HANDLER_ARGS)81*06589d6eSEmmanuel Vadot ad7418_sysctl_temp(SYSCTL_HANDLER_ARGS)
82*06589d6eSEmmanuel Vadot {
83*06589d6eSEmmanuel Vadot 	struct ad7418_softc *sc = arg1;
84*06589d6eSEmmanuel Vadot 	int temp;
85*06589d6eSEmmanuel Vadot 
86*06589d6eSEmmanuel Vadot 	sx_xlock(&sc->sc_lock);
87*06589d6eSEmmanuel Vadot 	ad7418_update(sc);
88*06589d6eSEmmanuel Vadot 	temp = (sc->sc_curtemp / 64) * 25;
89*06589d6eSEmmanuel Vadot 	sx_xunlock(&sc->sc_lock);
90*06589d6eSEmmanuel Vadot 	return sysctl_handle_int(oidp, &temp, 0, req);
91*06589d6eSEmmanuel Vadot }
92*06589d6eSEmmanuel Vadot 
93*06589d6eSEmmanuel Vadot static int
ad7418_sysctl_voltage(SYSCTL_HANDLER_ARGS)94*06589d6eSEmmanuel Vadot ad7418_sysctl_voltage(SYSCTL_HANDLER_ARGS)
95*06589d6eSEmmanuel Vadot {
96*06589d6eSEmmanuel Vadot 	struct ad7418_softc *sc = arg1;
97*06589d6eSEmmanuel Vadot 	int volt;
98*06589d6eSEmmanuel Vadot 
99*06589d6eSEmmanuel Vadot 	sx_xlock(&sc->sc_lock);
100*06589d6eSEmmanuel Vadot 	ad7418_update(sc);
101*06589d6eSEmmanuel Vadot 	volt = (sc->sc_curvolt >> 6) * 564 / 10;
102*06589d6eSEmmanuel Vadot 	sx_xunlock(&sc->sc_lock);
103*06589d6eSEmmanuel Vadot 	return sysctl_handle_int(oidp, &volt, 0, req);
104*06589d6eSEmmanuel Vadot }
105*06589d6eSEmmanuel Vadot 
106*06589d6eSEmmanuel Vadot static int
ad7418_attach(device_t dev)107*06589d6eSEmmanuel Vadot ad7418_attach(device_t dev)
108*06589d6eSEmmanuel Vadot {
109*06589d6eSEmmanuel Vadot 	struct ad7418_softc *sc = device_get_softc(dev);
110*06589d6eSEmmanuel Vadot 	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev);
111*06589d6eSEmmanuel Vadot 	struct sysctl_oid *tree = device_get_sysctl_tree(dev);
112*06589d6eSEmmanuel Vadot 	int conf;
113*06589d6eSEmmanuel Vadot 
114*06589d6eSEmmanuel Vadot 	sc->sc_dev = dev;
115*06589d6eSEmmanuel Vadot 	sc->sc_lastupdate = ticks - hz;
116*06589d6eSEmmanuel Vadot 
117*06589d6eSEmmanuel Vadot 	sx_init(&sc->sc_lock, "ad7418");
118*06589d6eSEmmanuel Vadot 
119*06589d6eSEmmanuel Vadot 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
120*06589d6eSEmmanuel Vadot 	    "temp", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0,
121*06589d6eSEmmanuel Vadot 	    ad7418_sysctl_temp, "I", "operating temperature");
122*06589d6eSEmmanuel Vadot 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
123*06589d6eSEmmanuel Vadot 	    "volt", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0,
124*06589d6eSEmmanuel Vadot 	    ad7418_sysctl_voltage, "I", "input voltage");
125*06589d6eSEmmanuel Vadot 
126*06589d6eSEmmanuel Vadot 	/* enable chip if configured in shutdown mode */
127*06589d6eSEmmanuel Vadot 	conf = ad7418_read_1(dev, AD7418_CONF);
128*06589d6eSEmmanuel Vadot 	if (conf >= 0 && (conf & AD7418_CONF_SHUTDOWN))
129*06589d6eSEmmanuel Vadot 		ad7418_write_1(dev, AD7418_CONF, conf &~ AD7418_CONF_SHUTDOWN);
130*06589d6eSEmmanuel Vadot 
131*06589d6eSEmmanuel Vadot 	return (0);
132*06589d6eSEmmanuel Vadot }
133*06589d6eSEmmanuel Vadot 
134*06589d6eSEmmanuel Vadot static int
ad7418_read_1(device_t dev,int reg)135*06589d6eSEmmanuel Vadot ad7418_read_1(device_t dev, int reg)
136*06589d6eSEmmanuel Vadot {
137*06589d6eSEmmanuel Vadot 	uint8_t addr = reg;
138*06589d6eSEmmanuel Vadot 	uint8_t data[1];
139*06589d6eSEmmanuel Vadot 	struct iic_msg msgs[2] = {
140*06589d6eSEmmanuel Vadot 	     { AD7418_ADDR, IIC_M_WR, 1, &addr },
141*06589d6eSEmmanuel Vadot 	     { AD7418_ADDR, IIC_M_RD, 1, data },
142*06589d6eSEmmanuel Vadot 	};
143*06589d6eSEmmanuel Vadot 	return iicbus_transfer(dev, msgs, 2) != 0 ? -1 : data[0];
144*06589d6eSEmmanuel Vadot }
145*06589d6eSEmmanuel Vadot 
146*06589d6eSEmmanuel Vadot static int
ad7418_write_1(device_t dev,int reg,int v)147*06589d6eSEmmanuel Vadot ad7418_write_1(device_t dev, int reg, int v)
148*06589d6eSEmmanuel Vadot {
149*06589d6eSEmmanuel Vadot 	/* NB: register pointer precedes actual data */
150*06589d6eSEmmanuel Vadot 	uint8_t data[2];
151*06589d6eSEmmanuel Vadot 	struct iic_msg msgs[1] = {
152*06589d6eSEmmanuel Vadot 	     { AD7418_ADDR, IIC_M_WR, 2, data },
153*06589d6eSEmmanuel Vadot 	};
154*06589d6eSEmmanuel Vadot 	data[0] = reg;
155*06589d6eSEmmanuel Vadot 	data[1] = v & 0xff;
156*06589d6eSEmmanuel Vadot 	return iicbus_transfer(dev, msgs, 1);
157*06589d6eSEmmanuel Vadot }
158*06589d6eSEmmanuel Vadot 
159*06589d6eSEmmanuel Vadot static void
ad7418_set_channel(struct ad7418_softc * sc,int chan)160*06589d6eSEmmanuel Vadot ad7418_set_channel(struct ad7418_softc *sc, int chan)
161*06589d6eSEmmanuel Vadot {
162*06589d6eSEmmanuel Vadot 	if (sc->sc_curchan == chan)
163*06589d6eSEmmanuel Vadot 		return;
164*06589d6eSEmmanuel Vadot 	ad7418_write_1(sc->sc_dev, AD7418_CONF,
165*06589d6eSEmmanuel Vadot 	    (ad7418_read_1(sc->sc_dev, AD7418_CONF) &~ AD7418_CONF_CHAN)|chan);
166*06589d6eSEmmanuel Vadot 	sc->sc_curchan = chan;
167*06589d6eSEmmanuel Vadot #if 0
168*06589d6eSEmmanuel Vadot 	/*
169*06589d6eSEmmanuel Vadot 	 * NB: Linux driver delays here but chip data sheet
170*06589d6eSEmmanuel Vadot 	 *     says nothing and things appear to work fine w/o
171*06589d6eSEmmanuel Vadot 	 *     a delay on channel change.
172*06589d6eSEmmanuel Vadot 	 */
173*06589d6eSEmmanuel Vadot 	/* let channel change settle, 1 tick should be 'nuf (need ~1ms) */
174*06589d6eSEmmanuel Vadot 	tsleep(sc, 0, "ad7418", hz/1000);
175*06589d6eSEmmanuel Vadot #endif
176*06589d6eSEmmanuel Vadot }
177*06589d6eSEmmanuel Vadot 
178*06589d6eSEmmanuel Vadot static int
ad7418_read_2(device_t dev,int reg)179*06589d6eSEmmanuel Vadot ad7418_read_2(device_t dev, int reg)
180*06589d6eSEmmanuel Vadot {
181*06589d6eSEmmanuel Vadot 	uint8_t addr = reg;
182*06589d6eSEmmanuel Vadot 	uint8_t data[2];
183*06589d6eSEmmanuel Vadot 	struct iic_msg msgs[2] = {
184*06589d6eSEmmanuel Vadot 	     { AD7418_ADDR, IIC_M_WR, 1, &addr },
185*06589d6eSEmmanuel Vadot 	     { AD7418_ADDR, IIC_M_RD, 2, data },
186*06589d6eSEmmanuel Vadot 	};
187*06589d6eSEmmanuel Vadot 	/* NB: D15..D8 precede D7..D0 per data sheet (Fig 12) */
188*06589d6eSEmmanuel Vadot 	return iicbus_transfer(dev, msgs, 2) != 0 ?
189*06589d6eSEmmanuel Vadot 		-1 : ((data[0] << 8) | data[1]);
190*06589d6eSEmmanuel Vadot }
191*06589d6eSEmmanuel Vadot 
192*06589d6eSEmmanuel Vadot static void
ad7418_update(struct ad7418_softc * sc)193*06589d6eSEmmanuel Vadot ad7418_update(struct ad7418_softc *sc)
194*06589d6eSEmmanuel Vadot {
195*06589d6eSEmmanuel Vadot 	int v;
196*06589d6eSEmmanuel Vadot 
197*06589d6eSEmmanuel Vadot 	sx_assert(&sc->sc_lock, SA_XLOCKED);
198*06589d6eSEmmanuel Vadot 	/* NB: no point in updating any faster than the chip */
199*06589d6eSEmmanuel Vadot 	if (ticks - sc->sc_lastupdate > hz) {
200*06589d6eSEmmanuel Vadot 		ad7418_set_channel(sc, AD7418_CHAN_TEMP);
201*06589d6eSEmmanuel Vadot 		v = ad7418_read_2(sc->sc_dev, AD7418_TEMP);
202*06589d6eSEmmanuel Vadot 		if (v >= 0)
203*06589d6eSEmmanuel Vadot 			sc->sc_curtemp = v;
204*06589d6eSEmmanuel Vadot 		ad7418_set_channel(sc, AD7418_CHAN_VOLT);
205*06589d6eSEmmanuel Vadot 		v = ad7418_read_2(sc->sc_dev, AD7418_VOLT);
206*06589d6eSEmmanuel Vadot 		if (v >= 0)
207*06589d6eSEmmanuel Vadot 			sc->sc_curvolt = v;
208*06589d6eSEmmanuel Vadot 		sc->sc_lastupdate = ticks;
209*06589d6eSEmmanuel Vadot 	}
210*06589d6eSEmmanuel Vadot }
211*06589d6eSEmmanuel Vadot 
212*06589d6eSEmmanuel Vadot static device_method_t ad7418_methods[] = {
213*06589d6eSEmmanuel Vadot 	DEVMETHOD(device_probe,		ad7418_probe),
214*06589d6eSEmmanuel Vadot 	DEVMETHOD(device_attach,	ad7418_attach),
215*06589d6eSEmmanuel Vadot 
216*06589d6eSEmmanuel Vadot 	DEVMETHOD_END
217*06589d6eSEmmanuel Vadot };
218*06589d6eSEmmanuel Vadot 
219*06589d6eSEmmanuel Vadot static driver_t ad7418_driver = {
220*06589d6eSEmmanuel Vadot 	"ad7418",
221*06589d6eSEmmanuel Vadot 	ad7418_methods,
222*06589d6eSEmmanuel Vadot 	sizeof(struct ad7418_softc),
223*06589d6eSEmmanuel Vadot };
224*06589d6eSEmmanuel Vadot 
225*06589d6eSEmmanuel Vadot DRIVER_MODULE(ad7418, iicbus, ad7418_driver, 0, 0);
226*06589d6eSEmmanuel Vadot MODULE_VERSION(ad7418, 1);
227*06589d6eSEmmanuel Vadot MODULE_DEPEND(ad7418, iicbus, 1, 1, 1);
228