1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) Andriy Gapon
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions, and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
19 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 */
28
29 #include <sys/cdefs.h>
30 #include "opt_platform.h"
31
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 #include <sys/kernel.h>
35 #include <sys/module.h>
36 #include <sys/sysctl.h>
37 #include <sys/systm.h>
38
39 #include <machine/bus.h>
40
41 #include <dev/iicbus/iicbus.h>
42 #include <dev/iicbus/iiconf.h>
43
44 #ifdef FDT
45 #include <dev/ofw/ofw_bus.h>
46 #include <dev/ofw/ofw_bus_subr.h>
47 #endif
48
49 /*
50 * Driver for MAX44009 Ambient Light Sensor with ADC.
51 */
52 #define REG_LUX_HIGH 0x03
53 #define REG_LUX_LOW 0x04
54
55 struct max44009_softc {
56 device_t sc_dev;
57 uint8_t sc_addr;
58 };
59
60 #ifdef FDT
61 static const struct ofw_compat_data compat_data[] = {
62 { "maxim,max44009", true },
63 { NULL, false },
64 };
65 #endif
66
67 static int
max44009_get_reading(device_t dev,u_int * reading)68 max44009_get_reading(device_t dev, u_int *reading)
69 {
70 struct iic_msg msgs[4];
71 struct max44009_softc *sc;
72 u_int val;
73 uint8_t reghi, reglo, valhi, vallo;
74 int error;
75
76 sc = device_get_softc(dev);
77
78 reghi = REG_LUX_HIGH;
79 reglo = REG_LUX_LOW;
80 msgs[0].slave = sc->sc_addr;
81 msgs[0].flags = IIC_M_WR | IIC_M_NOSTOP;
82 msgs[0].len = 1;
83 msgs[0].buf = ®hi;
84 msgs[1].slave = sc->sc_addr;
85 msgs[1].flags = IIC_M_RD | IIC_M_NOSTOP;
86 msgs[1].len = 1;
87 msgs[1].buf = &valhi;
88 msgs[2].slave = sc->sc_addr;
89 msgs[2].flags = IIC_M_WR | IIC_M_NOSTOP;
90 msgs[2].len = 1;
91 msgs[2].buf = ®lo;
92 msgs[3].slave = sc->sc_addr;
93 msgs[3].flags = IIC_M_RD;
94 msgs[3].len = 1;
95 msgs[3].buf = &vallo;
96
97 error = iicbus_transfer_excl(dev, msgs, nitems(msgs), IIC_INTRWAIT);
98 if (error != 0)
99 return (error);
100
101 val = ((valhi & 0x0f) << 4) | (vallo & 0x0f);
102 val <<= (valhi & 0xf0) >> 4;
103 val = val * 72 / 100;
104 *reading = val;
105 return (0);
106 }
107
108 static int
max44009_lux_sysctl(SYSCTL_HANDLER_ARGS)109 max44009_lux_sysctl(SYSCTL_HANDLER_ARGS)
110 {
111 device_t dev;
112 u_int reading;
113 int error, val;
114
115 if (req->oldptr != NULL) {
116 dev = arg1;
117 error = max44009_get_reading(dev, &reading);
118 if (error != 0)
119 return (EIO);
120 val = reading;
121 }
122 error = sysctl_handle_int(oidp, &val, 0, req);
123 return (error);
124 }
125
126 static int
max44009_probe(device_t dev)127 max44009_probe(device_t dev)
128 {
129 int rc;
130
131 #ifdef FDT
132 if (!ofw_bus_status_okay(dev))
133 return (ENXIO);
134 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data)
135 rc = BUS_PROBE_GENERIC;
136 else
137 #endif
138 rc = BUS_PROBE_NOWILDCARD;
139 device_set_desc(dev, "MAX44009 light intensity sensor");
140 return (rc);
141 }
142
143 static int
max44009_attach(device_t dev)144 max44009_attach(device_t dev)
145 {
146 struct max44009_softc *sc;
147 struct sysctl_ctx_list *ctx;
148 struct sysctl_oid *tree_node;
149 struct sysctl_oid_list *tree;
150
151 sc = device_get_softc(dev);
152 sc->sc_dev = dev;
153 sc->sc_addr = iicbus_get_addr(dev);
154
155 ctx = device_get_sysctl_ctx(dev);
156 tree_node = device_get_sysctl_tree(dev);
157 tree = SYSCTL_CHILDREN(tree_node);
158
159 SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "illuminance",
160 CTLTYPE_INT | CTLFLAG_RD, dev, 0,
161 max44009_lux_sysctl, "I", "Light intensity, lux");
162 return (0);
163 }
164
165 static int
max44009_detach(device_t dev)166 max44009_detach(device_t dev)
167 {
168 return (0);
169 }
170
171 static device_method_t max44009_methods[] = {
172 /* Device interface */
173 DEVMETHOD(device_probe, max44009_probe),
174 DEVMETHOD(device_attach, max44009_attach),
175 DEVMETHOD(device_detach, max44009_detach),
176
177 DEVMETHOD_END
178 };
179
180 static driver_t max44009_driver = {
181 "max44009",
182 max44009_methods,
183 sizeof(struct max44009_softc)
184 };
185
186 DRIVER_MODULE(max44009, iicbus, max44009_driver, 0, 0);
187 MODULE_DEPEND(max44009, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
188 MODULE_VERSION(max44009, 1);
189 IICBUS_FDT_PNP_INFO(compat_data);
190