1/* $OpenBSD: owsbm.c,v 1.11 2022/04/06 18:59:29 naddy 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 57struct 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 75int owsbm_match(struct device *, void *, void *); 76void owsbm_attach(struct device *, struct device *, void *); 77int owsbm_detach(struct device *, int); 78int owsbm_activate(struct device *, int); 79 80void owsbm_update(void *); 81 82const struct cfattach owsbm_ca = { 83 sizeof(struct owsbm_softc), 84 owsbm_match, 85 owsbm_attach, 86 owsbm_detach, 87 owsbm_activate 88}; 89 90struct cfdriver owsbm_cd = { 91 NULL, "owsbm", DV_DULL 92}; 93 94static const struct onewire_matchfam owsbm_fams[] = { 95 { ONEWIRE_FAMILY_DS2438 } 96}; 97 98int 99owsbm_match(struct device *parent, void *match, void *aux) 100{ 101 return (onewire_matchbyfam(aux, owsbm_fams, nitems(owsbm_fams))); 102} 103 104void 105owsbm_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 148int 149owsbm_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 162int 163owsbm_activate(struct device *self, int act) 164{ 165 return (0); 166} 167 168void 169owsbm_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 262done: 263 onewire_unlock(sc->sc_onewire); 264 rw_exit_write(&sc->sc_lock); 265} 266