1 /* $OpenBSD: owsbm.c,v 1.10 2015/03/14 03:38:48 jsg Exp $ */ 2 3 /* 4 * Copyright (c) 2007 Aaron Linville <aaron@linville.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 /* 20 * 1-Wire Smart Battery Monitor family type device driver. 21 * Provides on-board temperature, an A/D converter for voltage/current, 22 * current accumulator, elapsed time metter, and 40 bytes of nonvolatile 23 * memory. 24 */ 25 26 #include <sys/param.h> 27 #include <sys/systm.h> 28 #include <sys/device.h> 29 #include <sys/rwlock.h> 30 #include <sys/sensors.h> 31 32 #include <dev/onewire/onewiredevs.h> 33 #include <dev/onewire/onewirereg.h> 34 #include <dev/onewire/onewirevar.h> 35 36 /* Commands */ 37 #define DSSBM_CMD_READ_SCRATCHPAD 0xbe 38 #define DSSBM_CMD_WRITE_SCRATCHPAD 0x4e 39 #define DSSBM_CMD_COPY_SCRATCHPAD 0x48 40 41 #define DSSBM_CMD_RECALL_MEMORY 0xb8 42 43 #define DSSBM_CMD_CONVERT_T 0x44 44 #define DSSBM_CMD_CONVERT_V 0xb4 45 46 /* Scratchpad layout */ 47 #define DS2438_SP_STATUS 0 48 #define DS2438_SP_TEMP_LSB 1 49 #define DS2438_SP_TEMP_MSB 2 50 #define DS2438_SP_VOLT_LSB 3 51 #define DS2438_SP_VOLT_MSB 4 52 #define DS2438_SP_CURRENT_LSB 5 53 #define DS2438_SP_CURRENT_MSB 6 54 #define DS2438_SP_THRESHOLD 7 55 #define DS2438_SP_CRC 8 56 57 struct owsbm_softc { 58 struct device sc_dev; 59 60 void * sc_onewire; 61 u_int64_t sc_rom; 62 63 struct ksensordev sc_sensordev; 64 65 struct ksensor sc_temp; 66 struct ksensor sc_voltage_vdd; /* Battery, AD = 1*/ 67 struct ksensor sc_voltage_vad; /* General purpose, AD = 0 */ 68 struct ksensor sc_voltage_cr; /* Current Register */ 69 70 struct sensor_task *sc_sensortask; 71 72 struct rwlock sc_lock; 73 }; 74 75 int owsbm_match(struct device *, void *, void *); 76 void owsbm_attach(struct device *, struct device *, void *); 77 int owsbm_detach(struct device *, int); 78 int owsbm_activate(struct device *, int); 79 80 void owsbm_update(void *); 81 82 struct cfattach owsbm_ca = { 83 sizeof(struct owsbm_softc), 84 owsbm_match, 85 owsbm_attach, 86 owsbm_detach, 87 owsbm_activate 88 }; 89 90 struct cfdriver owsbm_cd = { 91 NULL, "owsbm", DV_DULL 92 }; 93 94 static const struct onewire_matchfam owsbm_fams[] = { 95 { ONEWIRE_FAMILY_DS2438 } 96 }; 97 98 int 99 owsbm_match(struct device *parent, void *match, void *aux) 100 { 101 return (onewire_matchbyfam(aux, owsbm_fams, nitems(owsbm_fams))); 102 } 103 104 void 105 owsbm_attach(struct device *parent, struct device *self, void *aux) 106 { 107 struct owsbm_softc *sc = (struct owsbm_softc *)self; 108 struct onewire_attach_args *oa = aux; 109 110 sc->sc_onewire = oa->oa_onewire; 111 sc->sc_rom = oa->oa_rom; 112 113 /* Initialize temp sensor */ 114 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 115 sizeof(sc->sc_sensordev.xname)); 116 sc->sc_temp.type = SENSOR_TEMP; 117 snprintf(sc->sc_temp.desc, sizeof(sc->sc_temp.desc), "sn %012llx", 118 ONEWIRE_ROM_SN(oa->oa_rom)); 119 sensor_attach(&sc->sc_sensordev, &sc->sc_temp); 120 121 /* Initialize voltage sensor */ 122 sc->sc_voltage_vdd.type = SENSOR_VOLTS_DC; 123 strlcpy(sc->sc_voltage_vdd.desc, "VDD", sizeof(sc->sc_voltage_vdd.desc)); 124 sensor_attach(&sc->sc_sensordev, &sc->sc_voltage_vdd); 125 126 /* Initialize voltage sensor */ 127 sc->sc_voltage_vad.type = SENSOR_VOLTS_DC; 128 strlcpy(sc->sc_voltage_vad.desc, "VAD", sizeof(sc->sc_voltage_vad.desc)); 129 sensor_attach(&sc->sc_sensordev, &sc->sc_voltage_vad); 130 131 /* Initialize the current sensor */ 132 sc->sc_voltage_cr.type = SENSOR_VOLTS_DC; 133 strlcpy(sc->sc_voltage_cr.desc, "CR", sizeof(sc->sc_voltage_cr.desc)); 134 sensor_attach(&sc->sc_sensordev, &sc->sc_voltage_cr); 135 136 sc->sc_sensortask = sensor_task_register(sc, owsbm_update, 10); 137 if (sc->sc_sensortask == NULL) { 138 printf(": unable to register update task\n"); 139 return; 140 } 141 142 sensordev_install(&sc->sc_sensordev); 143 144 rw_init(&sc->sc_lock, sc->sc_dev.dv_xname); 145 printf("\n"); 146 } 147 148 int 149 owsbm_detach(struct device *self, int flags) 150 { 151 struct owsbm_softc *sc = (struct owsbm_softc *)self; 152 153 rw_enter_write(&sc->sc_lock); 154 sensordev_deinstall(&sc->sc_sensordev); 155 if (sc->sc_sensortask != NULL) 156 sensor_task_unregister(sc->sc_sensortask); 157 rw_exit_write(&sc->sc_lock); 158 159 return (0); 160 } 161 162 int 163 owsbm_activate(struct device *self, int act) 164 { 165 return (0); 166 } 167 168 void 169 owsbm_update(void *arg) 170 { 171 struct owsbm_softc *sc = arg; 172 u_int8_t data[9]; 173 174 rw_enter_write(&sc->sc_lock); 175 onewire_lock(sc->sc_onewire, 0); 176 if (onewire_reset(sc->sc_onewire) != 0) 177 goto done; 178 179 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 180 onewire_write_byte(sc->sc_onewire, DSSBM_CMD_CONVERT_T); 181 if (onewire_reset(sc->sc_onewire) != 0) 182 goto done; 183 184 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 185 onewire_write_byte(sc->sc_onewire, DSSBM_CMD_CONVERT_V); 186 if (onewire_reset(sc->sc_onewire) != 0) 187 goto done; 188 189 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 190 /* Issue Recall Memory page 00h cmd */ 191 onewire_write_byte(sc->sc_onewire, DSSBM_CMD_RECALL_MEMORY); 192 onewire_write_byte(sc->sc_onewire, 0); 193 194 if (onewire_reset(sc->sc_onewire) != 0) 195 goto done; 196 197 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 198 /* Read page 0 of Memory Map from Scratchpad */ 199 onewire_write_byte(sc->sc_onewire, DSSBM_CMD_READ_SCRATCHPAD); 200 onewire_write_byte(sc->sc_onewire, 0); 201 onewire_read_block(sc->sc_onewire, data, 9); 202 if (onewire_crc(data, 8) == data[DS2438_SP_CRC]) { 203 sc->sc_temp.value = 273150000 + 204 (int)(((u_int16_t)data[DS2438_SP_TEMP_MSB] << 5) | 205 ((u_int16_t)data[DS2438_SP_TEMP_LSB] >> 3)) * 31250; 206 sc->sc_voltage_vdd.value = 207 (int)(((u_int16_t)data[DS2438_SP_VOLT_MSB] << 8) | 208 data[DS2438_SP_VOLT_LSB]) * 10000; 209 210 sc->sc_voltage_cr.value = 211 (int)(((u_int16_t)data[DS2438_SP_CURRENT_MSB] << 8) | 212 data[DS2438_SP_CURRENT_LSB]) * 244; 213 } 214 215 /* Reconfigure DS2438 to measure VAD */ 216 217 if (onewire_reset(sc->sc_onewire) != 0) 218 goto done; 219 220 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 221 onewire_write_byte(sc->sc_onewire, DSSBM_CMD_WRITE_SCRATCHPAD); 222 onewire_write_byte(sc->sc_onewire, 0); 223 onewire_write_byte(sc->sc_onewire, 0x7); /* AD = 0 */ 224 225 if (onewire_reset(sc->sc_onewire) != 0) 226 goto done; 227 228 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 229 onewire_write_byte(sc->sc_onewire, DSSBM_CMD_CONVERT_V); 230 if (onewire_reset(sc->sc_onewire) != 0) 231 goto done; 232 233 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 234 /* Issue Recall Memory page 00h cmd */ 235 onewire_write_byte(sc->sc_onewire, DSSBM_CMD_RECALL_MEMORY); 236 onewire_write_byte(sc->sc_onewire, 0); 237 238 if (onewire_reset(sc->sc_onewire) != 0) 239 goto done; 240 241 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 242 onewire_write_byte(sc->sc_onewire, DSSBM_CMD_READ_SCRATCHPAD); 243 onewire_write_byte(sc->sc_onewire, 0); 244 onewire_read_block(sc->sc_onewire, data, 9); 245 if (onewire_crc(data, 8) == data[8]) { 246 sc->sc_voltage_vad.value = 247 (int)(((u_int16_t)data[DS2438_SP_VOLT_MSB] << 8) | 248 data[DS2438_SP_VOLT_LSB]) * 10000; 249 } 250 251 /* Reconfigure back DS2438 to measure VDD (default) */ 252 253 if (onewire_reset(sc->sc_onewire) != 0) 254 goto done; 255 256 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 257 onewire_write_byte(sc->sc_onewire, DSSBM_CMD_WRITE_SCRATCHPAD); 258 onewire_write_byte(sc->sc_onewire, 0); 259 onewire_write_byte(sc->sc_onewire, 0xf); /* AD = 1 */ 260 onewire_reset(sc->sc_onewire); 261 262 done: 263 onewire_unlock(sc->sc_onewire); 264 rw_exit_write(&sc->sc_lock); 265 } 266