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