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