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