root/drivers/w1/slaves/w1_ds2438.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. w1_ds2438_get_page
  2. w1_ds2438_get_temperature
  3. w1_ds2438_change_config_bit
  4. w1_ds2438_get_voltage
  5. w1_ds2438_get_current
  6. iad_write
  7. iad_read
  8. page0_read
  9. temperature_read
  10. vad_read
  11. vdd_read

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

/* [<][>][^][v][top][bottom][index][help] */