linux/drivers/w1/slaves/w1_ds2438.c
<<
>>
Prefs
   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));
 519