linux/drivers/w1/slaves/w1_ds2805.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * w1_ds2805 - w1 family 0d (DS28E05) driver
   4 *
   5 * Copyright (c) 2016 Andrew Worsley amworsley@gmail.com
   6 */
   7
   8#include <linux/kernel.h>
   9#include <linux/module.h>
  10#include <linux/moduleparam.h>
  11#include <linux/device.h>
  12#include <linux/types.h>
  13#include <linux/delay.h>
  14
  15#include <linux/w1.h>
  16
  17#define W1_EEPROM_DS2805       0x0D
  18
  19#define W1_F0D_EEPROM_SIZE              128
  20#define W1_F0D_PAGE_BITS                3
  21#define W1_F0D_PAGE_SIZE                (1<<W1_F0D_PAGE_BITS)
  22#define W1_F0D_PAGE_MASK                0x0F
  23
  24#define W1_F0D_SCRATCH_BITS  1
  25#define W1_F0D_SCRATCH_SIZE  (1<<W1_F0D_SCRATCH_BITS)
  26#define W1_F0D_SCRATCH_MASK  (W1_F0D_SCRATCH_SIZE-1)
  27
  28#define W1_F0D_READ_EEPROM      0xF0
  29#define W1_F0D_WRITE_EEPROM     0x55
  30#define W1_F0D_RELEASE          0xFF
  31
  32#define W1_F0D_CS_OK            0xAA /* Chip Status Ok */
  33
  34#define W1_F0D_TPROG_MS         16
  35
  36#define W1_F0D_READ_RETRIES             10
  37#define W1_F0D_READ_MAXLEN              W1_F0D_EEPROM_SIZE
  38
  39/*
  40 * Check the file size bounds and adjusts count as needed.
  41 * This would not be needed if the file size didn't reset to 0 after a write.
  42 */
  43static inline size_t w1_f0d_fix_count(loff_t off, size_t count, size_t size)
  44{
  45        if (off > size)
  46                return 0;
  47
  48        if ((off + count) > size)
  49                return size - off;
  50
  51        return count;
  52}
  53
  54/*
  55 * Read a block from W1 ROM two times and compares the results.
  56 * If they are equal they are returned, otherwise the read
  57 * is repeated W1_F0D_READ_RETRIES times.
  58 *
  59 * count must not exceed W1_F0D_READ_MAXLEN.
  60 */
  61static int w1_f0d_readblock(struct w1_slave *sl, int off, int count, char *buf)
  62{
  63        u8 wrbuf[3];
  64        u8 cmp[W1_F0D_READ_MAXLEN];
  65        int tries = W1_F0D_READ_RETRIES;
  66
  67        do {
  68                wrbuf[0] = W1_F0D_READ_EEPROM;
  69                wrbuf[1] = off & 0x7f;
  70                wrbuf[2] = 0;
  71
  72                if (w1_reset_select_slave(sl))
  73                        return -1;
  74
  75                w1_write_block(sl->master, wrbuf, sizeof(wrbuf));
  76                w1_read_block(sl->master, buf, count);
  77
  78                if (w1_reset_select_slave(sl))
  79                        return -1;
  80
  81                w1_write_block(sl->master, wrbuf, sizeof(wrbuf));
  82                w1_read_block(sl->master, cmp, count);
  83
  84                if (!memcmp(cmp, buf, count))
  85                        return 0;
  86        } while (--tries);
  87
  88        dev_err(&sl->dev, "proof reading failed %d times\n",
  89                        W1_F0D_READ_RETRIES);
  90
  91        return -1;
  92}
  93
  94static ssize_t w1_f0d_read_bin(struct file *filp, struct kobject *kobj,
  95                               struct bin_attribute *bin_attr,
  96                               char *buf, loff_t off, size_t count)
  97{
  98        struct w1_slave *sl = kobj_to_w1_slave(kobj);
  99        int todo = count;
 100
 101        count = w1_f0d_fix_count(off, count, W1_F0D_EEPROM_SIZE);
 102        if (count == 0)
 103                return 0;
 104
 105        mutex_lock(&sl->master->mutex);
 106
 107        /* read directly from the EEPROM in chunks of W1_F0D_READ_MAXLEN */
 108        while (todo > 0) {
 109                int block_read;
 110
 111                if (todo >= W1_F0D_READ_MAXLEN)
 112                        block_read = W1_F0D_READ_MAXLEN;
 113                else
 114                        block_read = todo;
 115
 116                if (w1_f0d_readblock(sl, off, block_read, buf) < 0) {
 117                        count = -EIO;
 118                        break;
 119                }
 120
 121                todo -= W1_F0D_READ_MAXLEN;
 122                buf += W1_F0D_READ_MAXLEN;
 123                off += W1_F0D_READ_MAXLEN;
 124        }
 125
 126        mutex_unlock(&sl->master->mutex);
 127
 128        return count;
 129}
 130
 131/*
 132 * Writes to the scratchpad and reads it back for verification.
 133 * Then copies the scratchpad to EEPROM.
 134 * The data must be aligned at W1_F0D_SCRATCH_SIZE bytes and
 135 * must be W1_F0D_SCRATCH_SIZE bytes long.
 136 * The master must be locked.
 137 *
 138 * @param sl    The slave structure
 139 * @param addr  Address for the write
 140 * @param len   length must be <= (W1_F0D_PAGE_SIZE - (addr & W1_F0D_PAGE_MASK))
 141 * @param data  The data to write
 142 * @return      0=Success -1=failure
 143 */
 144static int w1_f0d_write(struct w1_slave *sl, int addr, int len, const u8 *data)
 145{
 146        int tries = W1_F0D_READ_RETRIES;
 147        u8 wrbuf[3];
 148        u8 rdbuf[W1_F0D_SCRATCH_SIZE];
 149        u8 cs;
 150
 151        if ((addr & 1) || (len != 2)) {
 152                dev_err(&sl->dev, "%s: bad addr/len -  addr=%#x len=%d\n",
 153                    __func__, addr, len);
 154                return -1;
 155        }
 156
 157retry:
 158
 159        /* Write the data to the scratchpad */
 160        if (w1_reset_select_slave(sl))
 161                return -1;
 162
 163        wrbuf[0] = W1_F0D_WRITE_EEPROM;
 164        wrbuf[1] = addr & 0xff;
 165        wrbuf[2] = 0xff; /* ?? from Example */
 166
 167        w1_write_block(sl->master, wrbuf, sizeof(wrbuf));
 168        w1_write_block(sl->master, data, len);
 169
 170        w1_read_block(sl->master, rdbuf, sizeof(rdbuf));
 171        /* Compare what was read against the data written */
 172        if ((rdbuf[0] != data[0]) || (rdbuf[1] != data[1])) {
 173
 174                if (--tries)
 175                        goto retry;
 176
 177                dev_err(&sl->dev,
 178                        "could not write to eeprom, scratchpad compare failed %d times\n",
 179                        W1_F0D_READ_RETRIES);
 180                pr_info("%s: rdbuf = %#x %#x data = %#x %#x\n",
 181                    __func__, rdbuf[0], rdbuf[1], data[0], data[1]);
 182
 183                return -1;
 184        }
 185
 186        /* Trigger write out to EEPROM */
 187        w1_write_8(sl->master, W1_F0D_RELEASE);
 188
 189        /* Sleep for tprog ms to wait for the write to complete */
 190        msleep(W1_F0D_TPROG_MS);
 191
 192        /* Check CS (Command Status) == 0xAA ? */
 193        cs = w1_read_8(sl->master);
 194        if (cs != W1_F0D_CS_OK) {
 195                dev_err(&sl->dev, "save to eeprom failed = CS=%#x\n", cs);
 196                return -1;
 197        }
 198
 199        return 0;
 200}
 201
 202static ssize_t w1_f0d_write_bin(struct file *filp, struct kobject *kobj,
 203                                struct bin_attribute *bin_attr,
 204                                char *buf, loff_t off, size_t count)
 205{
 206        struct w1_slave *sl = kobj_to_w1_slave(kobj);
 207        int addr, len;
 208        int copy;
 209
 210        count = w1_f0d_fix_count(off, count, W1_F0D_EEPROM_SIZE);
 211        if (count == 0)
 212                return 0;
 213
 214        mutex_lock(&sl->master->mutex);
 215
 216        /* Can only write data in blocks of the size of the scratchpad */
 217        addr = off;
 218        len = count;
 219        while (len > 0) {
 220
 221                /* if len too short or addr not aligned */
 222                if (len < W1_F0D_SCRATCH_SIZE || addr & W1_F0D_SCRATCH_MASK) {
 223                        char tmp[W1_F0D_SCRATCH_SIZE];
 224
 225                        /* read the block and update the parts to be written */
 226                        if (w1_f0d_readblock(sl, addr & ~W1_F0D_SCRATCH_MASK,
 227                                        W1_F0D_SCRATCH_SIZE, tmp)) {
 228                                count = -EIO;
 229                                goto out_up;
 230                        }
 231
 232                        /* copy at most to the boundary of the PAGE or len */
 233                        copy = W1_F0D_SCRATCH_SIZE -
 234                                (addr & W1_F0D_SCRATCH_MASK);
 235
 236                        if (copy > len)
 237                                copy = len;
 238
 239                        memcpy(&tmp[addr & W1_F0D_SCRATCH_MASK], buf, copy);
 240                        if (w1_f0d_write(sl, addr & ~W1_F0D_SCRATCH_MASK,
 241                                        W1_F0D_SCRATCH_SIZE, tmp) < 0) {
 242                                count = -EIO;
 243                                goto out_up;
 244                        }
 245                } else {
 246
 247                        copy = W1_F0D_SCRATCH_SIZE;
 248                        if (w1_f0d_write(sl, addr, copy, buf) < 0) {
 249                                count = -EIO;
 250                                goto out_up;
 251                        }
 252                }
 253                buf += copy;
 254                addr += copy;
 255                len -= copy;
 256        }
 257
 258out_up:
 259        mutex_unlock(&sl->master->mutex);
 260
 261        return count;
 262}
 263
 264static struct bin_attribute w1_f0d_bin_attr = {
 265        .attr = {
 266                .name = "eeprom",
 267                .mode = S_IRUGO | S_IWUSR,
 268        },
 269        .size = W1_F0D_EEPROM_SIZE,
 270        .read = w1_f0d_read_bin,
 271        .write = w1_f0d_write_bin,
 272};
 273
 274static int w1_f0d_add_slave(struct w1_slave *sl)
 275{
 276        return sysfs_create_bin_file(&sl->dev.kobj, &w1_f0d_bin_attr);
 277}
 278
 279static void w1_f0d_remove_slave(struct w1_slave *sl)
 280{
 281        sysfs_remove_bin_file(&sl->dev.kobj, &w1_f0d_bin_attr);
 282}
 283
 284static const struct w1_family_ops w1_f0d_fops = {
 285        .add_slave      = w1_f0d_add_slave,
 286        .remove_slave   = w1_f0d_remove_slave,
 287};
 288
 289static struct w1_family w1_family_0d = {
 290        .fid = W1_EEPROM_DS2805,
 291        .fops = &w1_f0d_fops,
 292};
 293
 294module_w1_family(w1_family_0d);
 295
 296MODULE_LICENSE("GPL");
 297MODULE_AUTHOR("Andrew Worsley amworsley@gmail.com");
 298MODULE_DESCRIPTION("w1 family 0d driver for DS2805, 1kb EEPROM");
 299