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