pcf8591_ofw.c revision 1.1
1/* $OpenBSD: pcf8591_ofw.c,v 1.1 2006/02/10 05:28:56 djm 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 29struct pcfadc_channel { 30 u_int chan_num; 31 struct sensor chan_sensor; 32}; 33 34struct 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}; 42 43int pcfadc_match(struct device *, void *, void *); 44void pcfadc_attach(struct device *, struct device *, void *); 45void pcfadc_refresh(void *); 46 47struct cfattach pcfadc_ca = { 48 sizeof(struct pcfadc_softc), pcfadc_match, pcfadc_attach 49}; 50 51struct cfdriver pcfadc_cd = { 52 NULL, "pcfadc", DV_DULL 53}; 54 55int 56pcfadc_match(struct device *parent, void *match, void *aux) 57{ 58 struct i2c_attach_args *ia = aux; 59 60 if (strcmp(ia->ia_name, "i2cpcf,8591") != 0) 61 return (0); 62 63 return (1); 64} 65 66void 67pcfadc_attach(struct device *parent, struct device *self, void *aux) 68{ 69 struct pcfadc_softc *sc = (struct pcfadc_softc *)self; 70 u_char chanuse[PCF8591_CHANNELS * 4], desc[PCF8591_CHANNELS * 32]; 71 u_char *cp; 72 u_int8_t junk[PCF8591_CHANNELS + 1]; 73 u_int32_t transinfo[PCF8591_CHANNELS * 4]; 74 struct i2c_attach_args *ia = aux; 75 int dlen, clen, tlen, node = *(int *)ia->ia_cookie; 76 u_int i; 77 78 if ((dlen = OF_getprop(node, "channels-description", desc, 79 sizeof(desc))) < 0) { 80 printf(": couldn't find \"channels-description\" property\n"); 81 return; 82 } 83 if (dlen > sizeof(desc) || desc[dlen - 1] != '\0') { 84 printf(": bad \"channels-description\" property\n"); 85 return; 86 } 87 if ((clen = OF_getprop(node, "channels-in-use", chanuse, 88 sizeof(chanuse))) < 0) { 89 printf(": couldn't find \"channels-in-use\" property\n"); 90 return; 91 } 92 if ((clen % 4) != 0) { 93 printf(": invalid \"channels-in-use\" length %d\n", clen); 94 return; 95 } 96 sc->sc_nchan = clen / 4; 97 if (sc->sc_nchan > PCF8591_CHANNELS) { 98 printf(": invalid number of channels (%d)\n", sc->sc_nchan); 99 return; 100 } 101 102 if ((tlen = OF_getprop(node, "tables", sc->sc_xlate, 103 sizeof(sc->sc_xlate))) < 0) { 104 printf(": couldn't find \"tables\" property\n"); 105 return; 106 } 107 /* We only support complete, single width tables */ 108 if (tlen != 256) { 109 printf(": invalid \"tables\" length %d\n", tlen); 110 return; 111 } 112 113 if ((tlen = OF_getprop(node, "translation", transinfo, 114 sizeof(transinfo))) < 0) { 115 printf(": couldn't find \"translation\" property\n"); 116 return; 117 } 118 if (tlen != (sc->sc_nchan * 4 * 4)) { 119 printf(": invalid \"translation\" length %d\n", tlen); 120 return; 121 } 122 123 cp = desc; 124 for (i = 0; i < sc->sc_nchan; i++) { 125 struct pcfadc_channel *chp = &sc->sc_channels[i]; 126 127 bzero(&chp->chan_sensor, sizeof(chp->chan_sensor)); 128 strlcpy(chp->chan_sensor.device, sc->sc_dev.dv_xname, 129 sizeof(chp->chan_sensor.device)); 130 chp->chan_sensor.type = SENSOR_TEMP; 131 132 if (cp >= desc + dlen) { 133 printf(": invalid \"channels-description\"\n"); 134 return; 135 } 136 strlcpy(chp->chan_sensor.desc, cp, 137 sizeof(chp->chan_sensor.desc)); 138 cp += strlen(cp) + 1; 139 140 /* 141 * We only support input temperature channels, with 142 * valid channel numbers, and basic (unscaled) translation 143 * 144 * XXX TODO: support voltage (type 2) channels and type 4 145 * (scaled) translation tables 146 */ 147 if (chanuse[(i * 4)] > PCF8591_CHANNELS || /* channel # */ 148 chanuse[(i * 4) + 1] != 0 || /* dir == input */ 149 chanuse[(i * 4) + 2] != 1 || /* type == temp */ 150 transinfo[(i * 4)] != 3 || /* xlate == table */ 151 transinfo[(i * 4) + 2] != 0 || /* no xlate offset */ 152 transinfo[(i * 4) + 3] != 0x100) { /* xlate tbl length */ 153 printf(": unsupported sensor %d\n", i); 154 return; 155 } 156 chp->chan_num = chanuse[(i * 4)]; 157 } 158 159 sc->sc_tag = ia->ia_tag; 160 sc->sc_addr = ia->ia_addr; 161 162 iic_acquire_bus(sc->sc_tag, 0); 163 164 /* Try a read now, so we can fail if it doesn't work */ 165 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 166 NULL, 0, junk, sc->sc_nchan + 1, 0)) { 167 printf(": read failed\n"); 168 iic_release_bus(sc->sc_tag, 0); 169 return; 170 } 171 172 iic_release_bus(sc->sc_tag, 0); 173 174 /* Initialize sensor data. */ 175 for (i = 0; i < sc->sc_nchan; i++) 176 if (!(sc->sc_channels[i].chan_sensor.flags & SENSOR_FINVALID)) 177 sensor_add(&sc->sc_channels[i].chan_sensor); 178 179 if (sensor_task_register(sc, pcfadc_refresh, 5)) { 180 printf(": unable to register update task\n"); 181 return; 182 } 183 184 printf("\n"); 185} 186 187void 188pcfadc_refresh(void *arg) 189{ 190 struct pcfadc_softc *sc = arg; 191 u_int i; 192 u_int8_t data[PCF8591_CHANNELS + 1]; 193 194 iic_acquire_bus(sc->sc_tag, 0); 195 /* NB: first byte out is stale, so read num_channels + 1 */ 196 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 197 NULL, 0, data, PCF8591_CHANNELS + 1, 0)) { 198 iic_release_bus(sc->sc_tag, 0); 199 return; 200 } 201 iic_release_bus(sc->sc_tag, 0); 202 203 /* XXX: so far this only supports temperature channels */ 204 for (i = 0; i < sc->sc_nchan; i++) { 205 struct pcfadc_channel *chp = &sc->sc_channels[i]; 206 207 if ((chp->chan_sensor.flags & SENSOR_FINVALID) != 0) 208 continue; 209 chp->chan_sensor.value = 273150000 + 1000000 * 210 sc->sc_xlate[data[1 + chp->chan_num]]; 211 } 212} 213 214