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