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 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 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 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 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 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