linux/drivers/w1/slaves/w1_ds2431.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * w1_ds2431.c - w1 family 2d (DS2431) driver
   4 *
   5 * Copyright (c) 2008 Bernhard Weirich <bernhard.weirich@riedel.net>
   6 *
   7 * Heavily inspired by w1_DS2433 driver from Ben Gardner <bgardner@wabtec.com>
   8 */
   9
  10#include <linux/kernel.h>
  11#include <linux/module.h>
  12#include <linux/moduleparam.h>
  13#include <linux/device.h>
  14#include <linux/types.h>
  15#include <linux/delay.h>
  16
  17#include <linux/w1.h>
  18
  19#define W1_EEPROM_DS2431        0x2D
  20
  21#define W1_F2D_EEPROM_SIZE              128
  22#define W1_F2D_PAGE_COUNT               4
  23#define W1_F2D_PAGE_BITS                5
  24#define W1_F2D_PAGE_SIZE                (1<<W1_F2D_PAGE_BITS)
  25#define W1_F2D_PAGE_MASK                0x1F
  26
  27#define W1_F2D_SCRATCH_BITS  3
  28#define W1_F2D_SCRATCH_SIZE  (1<<W1_F2D_SCRATCH_BITS)
  29#define W1_F2D_SCRATCH_MASK  (W1_F2D_SCRATCH_SIZE-1)
  30
  31#define W1_F2D_READ_EEPROM      0xF0
  32#define W1_F2D_WRITE_SCRATCH    0x0F
  33#define W1_F2D_READ_SCRATCH     0xAA
  34#define W1_F2D_COPY_SCRATCH     0x55
  35
  36
  37#define W1_F2D_TPROG_MS         11
  38
  39#define W1_F2D_READ_RETRIES             10
  40#define W1_F2D_READ_MAXLEN              8
  41
  42/*
  43 * Check the file size bounds and adjusts count as needed.
  44 * This would not be needed if the file size didn't reset to 0 after a write.
  45 */
  46static inline size_t w1_f2d_fix_count(loff_t off, size_t count, size_t size)
  47{
  48        if (off > size)
  49                return 0;
  50
  51        if ((off + count) > size)
  52                return size - off;
  53
  54        return count;
  55}
  56
  57/*
  58 * Read a block from W1 ROM two times and compares the results.
  59 * If they are equal they are returned, otherwise the read
  60 * is repeated W1_F2D_READ_RETRIES times.
  61 *
  62 * count must not exceed W1_F2D_READ_MAXLEN.
  63 */
  64static int w1_f2d_readblock(struct w1_slave *sl, int off, int count, char *buf)
  65{
  66        u8 wrbuf[3];
  67        u8 cmp[W1_F2D_READ_MAXLEN];
  68        int tries = W1_F2D_READ_RETRIES;
  69
  70        do {
  71                wrbuf[0] = W1_F2D_READ_EEPROM;
  72                wrbuf[1] = off & 0xff;
  73                wrbuf[2] = off >> 8;
  74
  75                if (w1_reset_select_slave(sl))
  76                        return -1;
  77
  78                w1_write_block(sl->master, wrbuf, 3);
  79                w1_read_block(sl->master, buf, count);
  80
  81                if (w1_reset_select_slave(sl))
  82                        return -1;
  83
  84                w1_write_block(sl->master, wrbuf, 3);
  85                w1_read_block(sl->master, cmp, count);
  86
  87                if (!memcmp(cmp, buf, count))
  88                        return 0;
  89        } while (--tries);
  90
  91        dev_err(&sl->dev, "proof reading failed %d times\n",
  92                        W1_F2D_READ_RETRIES);
  93
  94        return -1;
  95}
  96
  97static ssize_t eeprom_read(struct file *filp, struct kobject *kobj,
  98                           struct bin_attribute *bin_attr, char *buf,
  99                           loff_t off, size_t count)
 100{
 101        struct w1_slave *sl = kobj_to_w1_slave(kobj);
 102        int todo = count;
 103
 104        count = w1_f2d_fix_count(off, count, W1_F2D_EEPROM_SIZE);
 105        if (count == 0)
 106                return 0;
 107
 108        mutex_lock(&sl->master->bus_mutex);
 109
 110        /* read directly from the EEPROM in chunks of W1_F2D_READ_MAXLEN */
 111        while (todo > 0) {
 112                int block_read;
 113
 114                if (todo >= W1_F2D_READ_MAXLEN)
 115                        block_read = W1_F2D_READ_MAXLEN;
 116                else
 117                        block_read = todo;
 118
 119                if (w1_f2d_readblock(sl, off, block_read, buf) < 0)
 120                        count = -EIO;
 121
 122                todo -= W1_F2D_READ_MAXLEN;
 123                buf += W1_F2D_READ_MAXLEN;
 124                off += W1_F2D_READ_MAXLEN;
 125        }
 126
 127        mutex_unlock(&sl->master->bus_mutex);
 128
 129        return count;
 130}
 131
 132/*
 133 * Writes to the scratchpad and reads it back for verification.
 134 * Then copies the scratchpad to EEPROM.
 135 * The data must be aligned at W1_F2D_SCRATCH_SIZE bytes and
 136 * must be W1_F2D_SCRATCH_SIZE bytes long.
 137 * The master must be locked.
 138 *
 139 * @param sl    The slave structure
 140 * @param addr  Address for the write
 141 * @param len   length must be <= (W1_F2D_PAGE_SIZE - (addr & W1_F2D_PAGE_MASK))
 142 * @param data  The data to write
 143 * @return      0=Success -1=failure
 144 */
 145static int w1_f2d_write(struct w1_slave *sl, int addr, int len, const u8 *data)
 146{
 147        int tries = W1_F2D_READ_RETRIES;
 148        u8 wrbuf[4];
 149        u8 rdbuf[W1_F2D_SCRATCH_SIZE + 3];
 150        u8 es = (addr + len - 1) % W1_F2D_SCRATCH_SIZE;
 151
 152retry:
 153
 154        /* Write the data to the scratchpad */
 155        if (w1_reset_select_slave(sl))
 156                return -1;
 157
 158        wrbuf[0] = W1_F2D_WRITE_SCRATCH;
 159        wrbuf[1] = addr & 0xff;
 160        wrbuf[2] = addr >> 8;
 161
 162        w1_write_block(sl->master, wrbuf, 3);
 163        w1_write_block(sl->master, data, len);
 164
 165        /* Read the scratchpad and verify */
 166        if (w1_reset_select_slave(sl))
 167                return -1;
 168
 169        w1_write_8(sl->master, W1_F2D_READ_SCRATCH);
 170        w1_read_block(sl->master, rdbuf, len + 3);
 171
 172        /* Compare what was read against the data written */
 173        if ((rdbuf[0] != wrbuf[1]) || (rdbuf[1] != wrbuf[2]) ||
 174            (rdbuf[2] != es) || (memcmp(data, &rdbuf[3], len) != 0)) {
 175
 176                if (--tries)
 177                        goto retry;
 178
 179                dev_err(&sl->dev,
 180                        "could not write to eeprom, scratchpad compare failed %d times\n",
 181                        W1_F2D_READ_RETRIES);
 182
 183                return -1;
 184        }
 185
 186        /* Copy the scratchpad to EEPROM */
 187        if (w1_reset_select_slave(sl))
 188                return -1;
 189
 190        wrbuf[0] = W1_F2D_COPY_SCRATCH;
 191        wrbuf[3] = es;
 192        w1_write_block(sl->master, wrbuf, 4);
 193
 194        /* Sleep for tprog ms to wait for the write to complete */
 195        msleep(W1_F2D_TPROG_MS);
 196
 197        /* Reset the bus to wake up the EEPROM  */
 198        w1_reset_bus(sl->master);
 199
 200        return 0;
 201}
 202
 203static ssize_t eeprom_write(struct file *filp, struct kobject *kobj,
 204                            struct bin_attribute *bin_attr, char *buf,
 205                            loff_t off, size_t count)
 206{
 207        struct w1_slave *sl = kobj_to_w1_slave(kobj);
 208        int addr, len;
 209        int copy;
 210
 211        count = w1_f2d_fix_count(off, count, W1_F2D_EEPROM_SIZE);
 212        if (count == 0)
 213                return 0;
 214
 215        mutex_lock(&sl->master->bus_mutex);
 216
 217        /* Can only write data in blocks of the size of the scratchpad */
 218        addr = off;
 219        len = count;
 220        while (len > 0) {
 221
 222                /* if len too short or addr not aligned */
 223                if (len < W1_F2D_SCRATCH_SIZE || addr & W1_F2D_SCRATCH_MASK) {
 224                        char tmp[W1_F2D_SCRATCH_SIZE];
 225
 226                        /* read the block and update the parts to be written */
 227                        if (w1_f2d_readblock(sl, addr & ~W1_F2D_SCRATCH_MASK,
 228                                        W1_F2D_SCRATCH_SIZE, tmp)) {
 229                                count = -EIO;
 230                                goto out_up;
 231                        }
 232
 233                        /* copy at most to the boundary of the PAGE or len */
 234                        copy = W1_F2D_SCRATCH_SIZE -
 235                                (addr & W1_F2D_SCRATCH_MASK);
 236
 237                        if (copy > len)
 238                                copy = len;
 239
 240                        memcpy(&tmp[addr & W1_F2D_SCRATCH_MASK], buf, copy);
 241                        if (w1_f2d_write(sl, addr & ~W1_F2D_SCRATCH_MASK,
 242                                        W1_F2D_SCRATCH_SIZE, tmp) < 0) {
 243                                count = -EIO;
 244                                goto out_up;
 245                        }
 246                } else {
 247
 248                        copy = W1_F2D_SCRATCH_SIZE;
 249                        if (w1_f2d_write(sl, addr, copy, buf) < 0) {
 250                                count = -EIO;
 251                                goto out_up;
 252                        }
 253                }
 254                buf += copy;
 255                addr += copy;
 256                len -= copy;
 257        }
 258
 259out_up:
 260        mutex_unlock(&sl->master->bus_mutex);
 261
 262        return count;
 263}
 264
 265static BIN_ATTR_RW(eeprom, W1_F2D_EEPROM_SIZE);
 266
 267static struct bin_attribute *w1_f2d_bin_attrs[] = {
 268        &bin_attr_eeprom,
 269        NULL,
 270};
 271
 272static const struct attribute_group w1_f2d_group = {
 273        .bin_attrs = w1_f2d_bin_attrs,
 274};
 275
 276static const struct attribute_group *w1_f2d_groups[] = {
 277        &w1_f2d_group,
 278        NULL,
 279};
 280
 281static struct w1_family_ops w1_f2d_fops = {
 282        .groups         = w1_f2d_groups,
 283};
 284
 285static struct w1_family w1_family_2d = {
 286        .fid = W1_EEPROM_DS2431,
 287        .fops = &w1_f2d_fops,
 288};
 289module_w1_family(w1_family_2d);
 290
 291MODULE_AUTHOR("Bernhard Weirich <bernhard.weirich@riedel.net>");
 292MODULE_DESCRIPTION("w1 family 2d driver for DS2431, 1kb EEPROM");
 293MODULE_LICENSE("GPL");
 294MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS2431));
 295