1 /* $OpenBSD: pcf8591_ofw.c,v 1.6 2021/10/24 17:05:03 mpi Exp $ */ 2 3 /* 4 * Copyright (c) 2006 Damien Miller <djm@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/device.h> 22 #include <sys/sensors.h> 23 24 #include <dev/ofw/openfirm.h> 25 #include <dev/i2c/i2cvar.h> 26 27 #define PCF8591_CHANNELS 4 28 29 struct pcfadc_channel { 30 u_int chan_num; 31 struct ksensor chan_sensor; 32 }; 33 34 struct pcfadc_softc { 35 struct device sc_dev; 36 i2c_tag_t sc_tag; 37 i2c_addr_t sc_addr; 38 u_char sc_xlate[256]; 39 u_int sc_nchan; 40 struct pcfadc_channel sc_channels[PCF8591_CHANNELS]; 41 struct ksensordev sc_sensordev; 42 }; 43 44 int pcfadc_match(struct device *, void *, void *); 45 void pcfadc_attach(struct device *, struct device *, void *); 46 void pcfadc_refresh(void *); 47 48 const struct cfattach pcfadc_ca = { 49 sizeof(struct pcfadc_softc), pcfadc_match, pcfadc_attach 50 }; 51 52 struct cfdriver pcfadc_cd = { 53 NULL, "pcfadc", DV_DULL 54 }; 55 56 int 57 pcfadc_match(struct device *parent, void *match, void *aux) 58 { 59 struct i2c_attach_args *ia = aux; 60 61 if (strcmp(ia->ia_name, "i2cpcf,8591") != 0) 62 return (0); 63 64 return (1); 65 } 66 67 void 68 pcfadc_attach(struct device *parent, struct device *self, void *aux) 69 { 70 struct pcfadc_softc *sc = (struct pcfadc_softc *)self; 71 u_char chanuse[PCF8591_CHANNELS * 4], desc[PCF8591_CHANNELS * 32]; 72 u_char *cp; 73 u_int8_t junk[PCF8591_CHANNELS + 1]; 74 u_int32_t transinfo[PCF8591_CHANNELS * 4]; 75 struct i2c_attach_args *ia = aux; 76 int dlen, clen, tlen, node = *(int *)ia->ia_cookie; 77 u_int i; 78 79 if ((dlen = OF_getprop(node, "channels-description", desc, 80 sizeof(desc))) < 0) { 81 printf(": couldn't find \"channels-description\" property\n"); 82 return; 83 } 84 if (dlen > sizeof(desc) || desc[dlen - 1] != '\0') { 85 printf(": bad \"channels-description\" property\n"); 86 return; 87 } 88 if ((clen = OF_getprop(node, "channels-in-use", chanuse, 89 sizeof(chanuse))) < 0) { 90 printf(": couldn't find \"channels-in-use\" property\n"); 91 return; 92 } 93 if ((clen % 4) != 0) { 94 printf(": invalid \"channels-in-use\" length %d\n", clen); 95 return; 96 } 97 sc->sc_nchan = clen / 4; 98 if (sc->sc_nchan > PCF8591_CHANNELS) { 99 printf(": invalid number of channels (%d)\n", sc->sc_nchan); 100 return; 101 } 102 103 if ((tlen = OF_getprop(node, "tables", sc->sc_xlate, 104 sizeof(sc->sc_xlate))) < 0) { 105 printf(": couldn't find \"tables\" property\n"); 106 return; 107 } 108 /* We only support complete, single width tables */ 109 if (tlen != 256) { 110 printf(": invalid \"tables\" length %d\n", tlen); 111 return; 112 } 113 114 if ((tlen = OF_getprop(node, "translation", transinfo, 115 sizeof(transinfo))) < 0) { 116 printf(": couldn't find \"translation\" property\n"); 117 return; 118 } 119 if (tlen != (sc->sc_nchan * 4 * 4)) { 120 printf(": invalid \"translation\" length %d\n", tlen); 121 return; 122 } 123 124 cp = desc; 125 for (i = 0; i < sc->sc_nchan; i++) { 126 struct pcfadc_channel *chp = &sc->sc_channels[i]; 127 128 chp->chan_sensor.type = SENSOR_TEMP; 129 130 if (cp >= desc + dlen) { 131 printf(": invalid \"channels-description\"\n"); 132 return; 133 } 134 strlcpy(chp->chan_sensor.desc, cp, 135 sizeof(chp->chan_sensor.desc)); 136 cp += strlen(cp) + 1; 137 138 /* 139 * We only support input temperature channels, with 140 * valid channel numbers, and basic (unscaled) translation 141 * 142 * XXX TODO: support voltage (type 2) channels and type 4 143 * (scaled) translation tables 144 */ 145 if (chanuse[(i * 4)] > PCF8591_CHANNELS || /* channel # */ 146 chanuse[(i * 4) + 1] != 0 || /* dir == input */ 147 chanuse[(i * 4) + 2] != 1 || /* type == temp */ 148 transinfo[(i * 4)] != 3 || /* xlate == table */ 149 transinfo[(i * 4) + 2] != 0 || /* no xlate offset */ 150 transinfo[(i * 4) + 3] != 0x100) { /* xlate tbl length */ 151 printf(": unsupported sensor %d\n", i); 152 return; 153 } 154 chp->chan_num = chanuse[(i * 4)]; 155 } 156 157 sc->sc_tag = ia->ia_tag; 158 sc->sc_addr = ia->ia_addr; 159 160 iic_acquire_bus(sc->sc_tag, 0); 161 162 /* Try a read now, so we can fail if it doesn't work */ 163 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 164 NULL, 0, junk, sc->sc_nchan + 1, 0)) { 165 printf(": read failed\n"); 166 iic_release_bus(sc->sc_tag, 0); 167 return; 168 } 169 170 iic_release_bus(sc->sc_tag, 0); 171 172 /* Initialize sensor data. */ 173 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 174 sizeof(sc->sc_sensordev.xname)); 175 176 for (i = 0; i < sc->sc_nchan; i++) 177 sensor_attach(&sc->sc_sensordev, 178 &sc->sc_channels[i].chan_sensor); 179 180 if (sensor_task_register(sc, pcfadc_refresh, 5) == NULL) { 181 printf(": unable to register update task\n"); 182 return; 183 } 184 185 sensordev_install(&sc->sc_sensordev); 186 187 printf("\n"); 188 } 189 190 void 191 pcfadc_refresh(void *arg) 192 { 193 struct pcfadc_softc *sc = arg; 194 u_int i; 195 u_int8_t data[PCF8591_CHANNELS + 1]; 196 197 iic_acquire_bus(sc->sc_tag, 0); 198 /* NB: first byte out is stale, so read num_channels + 1 */ 199 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 200 NULL, 0, data, PCF8591_CHANNELS + 1, 0)) { 201 iic_release_bus(sc->sc_tag, 0); 202 return; 203 } 204 iic_release_bus(sc->sc_tag, 0); 205 206 /* XXX: so far this only supports temperature channels */ 207 for (i = 0; i < sc->sc_nchan; i++) { 208 struct pcfadc_channel *chp = &sc->sc_channels[i]; 209 210 chp->chan_sensor.value = 273150000 + 1000000 * 211 sc->sc_xlate[data[1 + chp->chan_num]]; 212 } 213 } 214 215