1 /* $OpenBSD: owsbm.c,v 1.6 2009/01/26 15:07:49 kettenis 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 *, enum devact); 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, 104 sizeof(owsbm_fams) /sizeof(owsbm_fams[0]))); 105 } 106 107 void 108 owsbm_attach(struct device *parent, struct device *self, void *aux) 109 { 110 struct owsbm_softc *sc = (struct owsbm_softc *)self; 111 struct onewire_attach_args *oa = aux; 112 113 sc->sc_onewire = oa->oa_onewire; 114 sc->sc_rom = oa->oa_rom; 115 116 /* Initialize temp sensor */ 117 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 118 sizeof(sc->sc_sensordev.xname)); 119 sc->sc_temp.type = SENSOR_TEMP; 120 snprintf(sc->sc_temp.desc, sizeof(sc->sc_temp.desc), "sn %012llx", 121 ONEWIRE_ROM_SN(oa->oa_rom)); 122 sensor_attach(&sc->sc_sensordev, &sc->sc_temp); 123 124 /* Initialize voltage sensor */ 125 sc->sc_voltage_vdd.type = SENSOR_VOLTS_DC; 126 strlcpy(sc->sc_voltage_vdd.desc, "VDD", sizeof(sc->sc_voltage_vdd.desc)); 127 sensor_attach(&sc->sc_sensordev, &sc->sc_voltage_vdd); 128 129 /* Initialize voltage sensor */ 130 sc->sc_voltage_vad.type = SENSOR_VOLTS_DC; 131 strlcpy(sc->sc_voltage_vad.desc, "VAD", sizeof(sc->sc_voltage_vad.desc)); 132 sensor_attach(&sc->sc_sensordev, &sc->sc_voltage_vad); 133 134 /* Initialize the current sensor */ 135 sc->sc_voltage_cr.type = SENSOR_VOLTS_DC; 136 strlcpy(sc->sc_voltage_cr.desc, "CR", sizeof(sc->sc_voltage_cr.desc)); 137 sensor_attach(&sc->sc_sensordev, &sc->sc_voltage_cr); 138 139 sc->sc_sensortask = sensor_task_register(sc, owsbm_update, 10); 140 if (sc->sc_sensortask == NULL) { 141 printf(": unable to register update task\n"); 142 return; 143 } 144 145 sensordev_install(&sc->sc_sensordev); 146 147 rw_init(&sc->sc_lock, sc->sc_dev.dv_xname); 148 printf("\n"); 149 } 150 151 int 152 owsbm_detach(struct device *self, int flags) 153 { 154 struct owsbm_softc *sc = (struct owsbm_softc *)self; 155 156 rw_enter_write(&sc->sc_lock); 157 sensordev_deinstall(&sc->sc_sensordev); 158 if (sc->sc_sensortask != NULL) 159 sensor_task_unregister(sc->sc_sensortask); 160 rw_exit_write(&sc->sc_lock); 161 162 return (0); 163 } 164 165 int 166 owsbm_activate(struct device *self, enum devact act) 167 { 168 return (0); 169 } 170 171 void 172 owsbm_update(void *arg) 173 { 174 struct owsbm_softc *sc = arg; 175 u_int8_t data[9]; 176 177 rw_enter_write(&sc->sc_lock); 178 onewire_lock(sc->sc_onewire, 0); 179 if (onewire_reset(sc->sc_onewire) != 0) 180 goto done; 181 182 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 183 onewire_write_byte(sc->sc_onewire, DSSBM_CMD_CONVERT_T); 184 if (onewire_reset(sc->sc_onewire) != 0) 185 goto done; 186 187 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 188 onewire_write_byte(sc->sc_onewire, DSSBM_CMD_CONVERT_V); 189 if (onewire_reset(sc->sc_onewire) != 0) 190 goto done; 191 192 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 193 /* Issue Recall Memory page 00h cmd */ 194 onewire_write_byte(sc->sc_onewire, DSSBM_CMD_RECALL_MEMORY); 195 onewire_write_byte(sc->sc_onewire, 0); 196 197 if (onewire_reset(sc->sc_onewire) != 0) 198 goto done; 199 200 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 201 /* Read page 0 of Memory Map from Scratchpad */ 202 onewire_write_byte(sc->sc_onewire, DSSBM_CMD_READ_SCRATCHPAD); 203 onewire_write_byte(sc->sc_onewire, 0); 204 onewire_read_block(sc->sc_onewire, data, 9); 205 if (onewire_crc(data, 8) == data[DS2438_SP_CRC]) { 206 sc->sc_temp.value = 273150000 + 207 (int)(((u_int16_t)data[DS2438_SP_TEMP_MSB] << 5) | 208 ((u_int16_t)data[DS2438_SP_TEMP_LSB] >> 3)) * 31250; 209 sc->sc_voltage_vdd.value = 210 (int)(((u_int16_t)data[DS2438_SP_VOLT_MSB] << 8) | 211 data[DS2438_SP_VOLT_LSB]) * 10000; 212 213 sc->sc_voltage_cr.value = 214 (int)(((u_int16_t)data[DS2438_SP_CURRENT_MSB] << 8) | 215 data[DS2438_SP_CURRENT_LSB]) * 244; 216 } 217 218 /* Reconfigure DS2438 to measure VAD */ 219 220 if (onewire_reset(sc->sc_onewire) != 0) 221 goto done; 222 223 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 224 onewire_write_byte(sc->sc_onewire, DSSBM_CMD_WRITE_SCRATCHPAD); 225 onewire_write_byte(sc->sc_onewire, 0); 226 onewire_write_byte(sc->sc_onewire, 0x7); /* AD = 0 */ 227 228 if (onewire_reset(sc->sc_onewire) != 0) 229 goto done; 230 231 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 232 onewire_write_byte(sc->sc_onewire, DSSBM_CMD_CONVERT_V); 233 if (onewire_reset(sc->sc_onewire) != 0) 234 goto done; 235 236 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 237 /* Issue Recall Memory page 00h cmd */ 238 onewire_write_byte(sc->sc_onewire, DSSBM_CMD_RECALL_MEMORY); 239 onewire_write_byte(sc->sc_onewire, 0); 240 241 if (onewire_reset(sc->sc_onewire) != 0) 242 goto done; 243 244 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 245 onewire_write_byte(sc->sc_onewire, DSSBM_CMD_READ_SCRATCHPAD); 246 onewire_write_byte(sc->sc_onewire, 0); 247 onewire_read_block(sc->sc_onewire, data, 9); 248 if (onewire_crc(data, 8) == data[8]) { 249 sc->sc_voltage_vad.value = 250 (int)(((u_int16_t)data[DS2438_SP_VOLT_MSB] << 8) | 251 data[DS2438_SP_VOLT_LSB]) * 10000; 252 } 253 254 /* Reconfigure back DS2438 to measure VDD (default) */ 255 256 if (onewire_reset(sc->sc_onewire) != 0) 257 goto done; 258 259 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 260 onewire_write_byte(sc->sc_onewire, DSSBM_CMD_WRITE_SCRATCHPAD); 261 onewire_write_byte(sc->sc_onewire, 0); 262 onewire_write_byte(sc->sc_onewire, 0xf); /* AD = 1 */ 263 onewire_reset(sc->sc_onewire); 264 265 done: 266 onewire_unlock(sc->sc_onewire); 267 rw_exit_write(&sc->sc_lock); 268 } 269