1 /* 2 * 1-Wire implementation for the ds2438 chip 3 * 4 * Copyright (c) 2017 Mariusz Bialonczyk <manio@skyboo.net> 5 * 6 * This source code is licensed under the GNU General Public License, 7 * Version 2. See the file COPYING for more details. 8 */ 9 10 #include <linux/kernel.h> 11 #include <linux/module.h> 12 #include <linux/device.h> 13 #include <linux/types.h> 14 #include <linux/delay.h> 15 16 #include <linux/w1.h> 17 18 #define W1_FAMILY_DS2438 0x26 19 20 #define W1_DS2438_RETRIES 3 21 22 /* Memory commands */ 23 #define W1_DS2438_READ_SCRATCH 0xBE 24 #define W1_DS2438_WRITE_SCRATCH 0x4E 25 #define W1_DS2438_COPY_SCRATCH 0x48 26 #define W1_DS2438_RECALL_MEMORY 0xB8 27 /* Register commands */ 28 #define W1_DS2438_CONVERT_TEMP 0x44 29 #define W1_DS2438_CONVERT_VOLTAGE 0xB4 30 31 #define DS2438_PAGE_SIZE 8 32 #define DS2438_ADC_INPUT_VAD 0 33 #define DS2438_ADC_INPUT_VDD 1 34 #define DS2438_MAX_CONVERSION_TIME 10 /* ms */ 35 36 /* Page #0 definitions */ 37 #define DS2438_STATUS_REG 0x00 /* Status/Configuration Register */ 38 #define DS2438_STATUS_IAD (1 << 0) /* Current A/D Control Bit */ 39 #define DS2438_STATUS_CA (1 << 1) /* Current Accumulator Configuration */ 40 #define DS2438_STATUS_EE (1 << 2) /* Current Accumulator Shadow Selector bit */ 41 #define DS2438_STATUS_AD (1 << 3) /* Voltage A/D Input Select Bit */ 42 #define DS2438_STATUS_TB (1 << 4) /* Temperature Busy Flag */ 43 #define DS2438_STATUS_NVB (1 << 5) /* Nonvolatile Memory Busy Flag */ 44 #define DS2438_STATUS_ADB (1 << 6) /* A/D Converter Busy Flag */ 45 46 #define DS2438_TEMP_LSB 0x01 47 #define DS2438_TEMP_MSB 0x02 48 #define DS2438_VOLTAGE_LSB 0x03 49 #define DS2438_VOLTAGE_MSB 0x04 50 #define DS2438_CURRENT_LSB 0x05 51 #define DS2438_CURRENT_MSB 0x06 52 #define DS2438_THRESHOLD 0x07 53 54 static int w1_ds2438_get_page(struct w1_slave *sl, int pageno, u8 *buf) 55 { 56 unsigned int retries = W1_DS2438_RETRIES; 57 u8 w1_buf[2]; 58 u8 crc; 59 size_t count; 60 61 while (retries--) { 62 crc = 0; 63 64 if (w1_reset_select_slave(sl)) 65 continue; 66 w1_buf[0] = W1_DS2438_RECALL_MEMORY; 67 w1_buf[1] = 0x00; 68 w1_write_block(sl->master, w1_buf, 2); 69 70 if (w1_reset_select_slave(sl)) 71 continue; 72 w1_buf[0] = W1_DS2438_READ_SCRATCH; 73 w1_buf[1] = 0x00; 74 w1_write_block(sl->master, w1_buf, 2); 75 76 count = w1_read_block(sl->master, buf, DS2438_PAGE_SIZE + 1); 77 if (count == DS2438_PAGE_SIZE + 1) { 78 crc = w1_calc_crc8(buf, DS2438_PAGE_SIZE); 79 80 /* check for correct CRC */ 81 if ((u8)buf[DS2438_PAGE_SIZE] == crc) 82 return 0; 83 } 84 } 85 return -1; 86 } 87 88 static int w1_ds2438_get_temperature(struct w1_slave *sl, int16_t *temperature) 89 { 90 unsigned int retries = W1_DS2438_RETRIES; 91 u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/]; 92 unsigned int tm = DS2438_MAX_CONVERSION_TIME; 93 unsigned long sleep_rem; 94 int ret; 95 96 mutex_lock(&sl->master->bus_mutex); 97 98 while (retries--) { 99 if (w1_reset_select_slave(sl)) 100 continue; 101 w1_write_8(sl->master, W1_DS2438_CONVERT_TEMP); 102 103 mutex_unlock(&sl->master->bus_mutex); 104 sleep_rem = msleep_interruptible(tm); 105 if (sleep_rem != 0) { 106 ret = -1; 107 goto post_unlock; 108 } 109 110 if (mutex_lock_interruptible(&sl->master->bus_mutex) != 0) { 111 ret = -1; 112 goto post_unlock; 113 } 114 115 break; 116 } 117 118 if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) { 119 *temperature = (((int16_t) w1_buf[DS2438_TEMP_MSB]) << 8) | ((uint16_t) w1_buf[DS2438_TEMP_LSB]); 120 ret = 0; 121 } else 122 ret = -1; 123 124 mutex_unlock(&sl->master->bus_mutex); 125 126 post_unlock: 127 return ret; 128 } 129 130 static int w1_ds2438_change_config_bit(struct w1_slave *sl, u8 mask, u8 value) 131 { 132 unsigned int retries = W1_DS2438_RETRIES; 133 u8 w1_buf[3]; 134 u8 status; 135 int perform_write = 0; 136 137 while (retries--) { 138 if (w1_reset_select_slave(sl)) 139 continue; 140 w1_buf[0] = W1_DS2438_RECALL_MEMORY; 141 w1_buf[1] = 0x00; 142 w1_write_block(sl->master, w1_buf, 2); 143 144 if (w1_reset_select_slave(sl)) 145 continue; 146 w1_buf[0] = W1_DS2438_READ_SCRATCH; 147 w1_buf[1] = 0x00; 148 w1_write_block(sl->master, w1_buf, 2); 149 150 /* reading one byte of result */ 151 status = w1_read_8(sl->master); 152 153 /* if bit0=1, set a value to a mask for easy compare */ 154 if (value) 155 value = mask; 156 157 if ((status & mask) == value) 158 return 0; /* already set as requested */ 159 else { 160 /* changing bit */ 161 status ^= mask; 162 perform_write = 1; 163 } 164 break; 165 } 166 167 if (perform_write) { 168 retries = W1_DS2438_RETRIES; 169 while (retries--) { 170 if (w1_reset_select_slave(sl)) 171 continue; 172 w1_buf[0] = W1_DS2438_WRITE_SCRATCH; 173 w1_buf[1] = 0x00; 174 w1_buf[2] = status; 175 w1_write_block(sl->master, w1_buf, 3); 176 177 if (w1_reset_select_slave(sl)) 178 continue; 179 w1_buf[0] = W1_DS2438_COPY_SCRATCH; 180 w1_buf[1] = 0x00; 181 w1_write_block(sl->master, w1_buf, 2); 182 183 return 0; 184 } 185 } 186 return -1; 187 } 188 189 static int w1_ds2438_get_voltage(struct w1_slave *sl, 190 int adc_input, uint16_t *voltage) 191 { 192 unsigned int retries = W1_DS2438_RETRIES; 193 u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/]; 194 unsigned int tm = DS2438_MAX_CONVERSION_TIME; 195 unsigned long sleep_rem; 196 int ret; 197 198 mutex_lock(&sl->master->bus_mutex); 199 200 if (w1_ds2438_change_config_bit(sl, DS2438_STATUS_AD, adc_input)) { 201 ret = -1; 202 goto pre_unlock; 203 } 204 205 while (retries--) { 206 if (w1_reset_select_slave(sl)) 207 continue; 208 w1_write_8(sl->master, W1_DS2438_CONVERT_VOLTAGE); 209 210 mutex_unlock(&sl->master->bus_mutex); 211 sleep_rem = msleep_interruptible(tm); 212 if (sleep_rem != 0) { 213 ret = -1; 214 goto post_unlock; 215 } 216 217 if (mutex_lock_interruptible(&sl->master->bus_mutex) != 0) { 218 ret = -1; 219 goto post_unlock; 220 } 221 222 break; 223 } 224 225 if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) { 226 *voltage = (((uint16_t) w1_buf[DS2438_VOLTAGE_MSB]) << 8) | ((uint16_t) w1_buf[DS2438_VOLTAGE_LSB]); 227 ret = 0; 228 } else 229 ret = -1; 230 231 pre_unlock: 232 mutex_unlock(&sl->master->bus_mutex); 233 234 post_unlock: 235 return ret; 236 } 237 238 static int w1_ds2438_get_current(struct w1_slave *sl, int16_t *voltage) 239 { 240 u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/]; 241 int ret; 242 243 mutex_lock(&sl->master->bus_mutex); 244 245 if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) { 246 /* The voltage measured across current sense resistor RSENS. */ 247 *voltage = (((int16_t) w1_buf[DS2438_CURRENT_MSB]) << 8) | ((int16_t) w1_buf[DS2438_CURRENT_LSB]); 248 ret = 0; 249 } else 250 ret = -1; 251 252 mutex_unlock(&sl->master->bus_mutex); 253 254 return ret; 255 } 256 257 static ssize_t iad_write(struct file *filp, struct kobject *kobj, 258 struct bin_attribute *bin_attr, char *buf, 259 loff_t off, size_t count) 260 { 261 struct w1_slave *sl = kobj_to_w1_slave(kobj); 262 int ret; 263 264 if (count != 1 || off != 0) 265 return -EFAULT; 266 267 mutex_lock(&sl->master->bus_mutex); 268 269 if (w1_ds2438_change_config_bit(sl, DS2438_STATUS_IAD, *buf & 0x01) == 0) 270 ret = 1; 271 else 272 ret = -EIO; 273 274 mutex_unlock(&sl->master->bus_mutex); 275 276 return ret; 277 } 278 279 static ssize_t iad_read(struct file *filp, struct kobject *kobj, 280 struct bin_attribute *bin_attr, char *buf, 281 loff_t off, size_t count) 282 { 283 struct w1_slave *sl = kobj_to_w1_slave(kobj); 284 int ret; 285 int16_t voltage; 286 287 if (off != 0) 288 return 0; 289 if (!buf) 290 return -EINVAL; 291 292 if (w1_ds2438_get_current(sl, &voltage) == 0) { 293 ret = snprintf(buf, count, "%i\n", voltage); 294 } else 295 ret = -EIO; 296 297 return ret; 298 } 299 300 static ssize_t page0_read(struct file *filp, struct kobject *kobj, 301 struct bin_attribute *bin_attr, char *buf, 302 loff_t off, size_t count) 303 { 304 struct w1_slave *sl = kobj_to_w1_slave(kobj); 305 int ret; 306 u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/]; 307 308 if (off != 0) 309 return 0; 310 if (!buf) 311 return -EINVAL; 312 313 mutex_lock(&sl->master->bus_mutex); 314 315 /* Read no more than page0 size */ 316 if (count > DS2438_PAGE_SIZE) 317 count = DS2438_PAGE_SIZE; 318 319 if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) { 320 memcpy(buf, &w1_buf, count); 321 ret = count; 322 } else 323 ret = -EIO; 324 325 mutex_unlock(&sl->master->bus_mutex); 326 327 return ret; 328 } 329 330 static ssize_t temperature_read(struct file *filp, struct kobject *kobj, 331 struct bin_attribute *bin_attr, char *buf, 332 loff_t off, size_t count) 333 { 334 struct w1_slave *sl = kobj_to_w1_slave(kobj); 335 int ret; 336 int16_t temp; 337 338 if (off != 0) 339 return 0; 340 if (!buf) 341 return -EINVAL; 342 343 if (w1_ds2438_get_temperature(sl, &temp) == 0) { 344 ret = snprintf(buf, count, "%i\n", temp); 345 } else 346 ret = -EIO; 347 348 return ret; 349 } 350 351 static ssize_t vad_read(struct file *filp, struct kobject *kobj, 352 struct bin_attribute *bin_attr, char *buf, 353 loff_t off, size_t count) 354 { 355 struct w1_slave *sl = kobj_to_w1_slave(kobj); 356 int ret; 357 uint16_t voltage; 358 359 if (off != 0) 360 return 0; 361 if (!buf) 362 return -EINVAL; 363 364 if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VAD, &voltage) == 0) { 365 ret = snprintf(buf, count, "%u\n", voltage); 366 } else 367 ret = -EIO; 368 369 return ret; 370 } 371 372 static ssize_t vdd_read(struct file *filp, struct kobject *kobj, 373 struct bin_attribute *bin_attr, char *buf, 374 loff_t off, size_t count) 375 { 376 struct w1_slave *sl = kobj_to_w1_slave(kobj); 377 int ret; 378 uint16_t voltage; 379 380 if (off != 0) 381 return 0; 382 if (!buf) 383 return -EINVAL; 384 385 if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VDD, &voltage) == 0) { 386 ret = snprintf(buf, count, "%u\n", voltage); 387 } else 388 ret = -EIO; 389 390 return ret; 391 } 392 393 static BIN_ATTR(iad, S_IRUGO | S_IWUSR | S_IWGRP, iad_read, iad_write, 0); 394 static BIN_ATTR_RO(page0, DS2438_PAGE_SIZE); 395 static BIN_ATTR_RO(temperature, 0/* real length varies */); 396 static BIN_ATTR_RO(vad, 0/* real length varies */); 397 static BIN_ATTR_RO(vdd, 0/* real length varies */); 398 399 static struct bin_attribute *w1_ds2438_bin_attrs[] = { 400 &bin_attr_iad, 401 &bin_attr_page0, 402 &bin_attr_temperature, 403 &bin_attr_vad, 404 &bin_attr_vdd, 405 NULL, 406 }; 407 408 static const struct attribute_group w1_ds2438_group = { 409 .bin_attrs = w1_ds2438_bin_attrs, 410 }; 411 412 static const struct attribute_group *w1_ds2438_groups[] = { 413 &w1_ds2438_group, 414 NULL, 415 }; 416 417 static struct w1_family_ops w1_ds2438_fops = { 418 .groups = w1_ds2438_groups, 419 }; 420 421 static struct w1_family w1_ds2438_family = { 422 .fid = W1_FAMILY_DS2438, 423 .fops = &w1_ds2438_fops, 424 }; 425 module_w1_family(w1_ds2438_family); 426 427 MODULE_LICENSE("GPL"); 428 MODULE_AUTHOR("Mariusz Bialonczyk <manio@skyboo.net>"); 429 MODULE_DESCRIPTION("1-wire driver for Maxim/Dallas DS2438 Smart Battery Monitor"); 430 MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2438)); 431