Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * w1_ds2805 - w1 family 0d (DS28E05) driver
0004  *
0005  * Copyright (c) 2016 Andrew Worsley amworsley@gmail.com
0006  */
0007 
0008 #include <linux/kernel.h>
0009 #include <linux/module.h>
0010 #include <linux/moduleparam.h>
0011 #include <linux/device.h>
0012 #include <linux/types.h>
0013 #include <linux/delay.h>
0014 
0015 #include <linux/w1.h>
0016 
0017 #define W1_EEPROM_DS2805       0x0D
0018 
0019 #define W1_F0D_EEPROM_SIZE      128
0020 #define W1_F0D_PAGE_BITS        3
0021 #define W1_F0D_PAGE_SIZE        (1<<W1_F0D_PAGE_BITS)
0022 #define W1_F0D_PAGE_MASK        0x0F
0023 
0024 #define W1_F0D_SCRATCH_BITS  1
0025 #define W1_F0D_SCRATCH_SIZE  (1<<W1_F0D_SCRATCH_BITS)
0026 #define W1_F0D_SCRATCH_MASK  (W1_F0D_SCRATCH_SIZE-1)
0027 
0028 #define W1_F0D_READ_EEPROM  0xF0
0029 #define W1_F0D_WRITE_EEPROM 0x55
0030 #define W1_F0D_RELEASE      0xFF
0031 
0032 #define W1_F0D_CS_OK        0xAA /* Chip Status Ok */
0033 
0034 #define W1_F0D_TPROG_MS     16
0035 
0036 #define W1_F0D_READ_RETRIES     10
0037 #define W1_F0D_READ_MAXLEN      W1_F0D_EEPROM_SIZE
0038 
0039 /*
0040  * Check the file size bounds and adjusts count as needed.
0041  * This would not be needed if the file size didn't reset to 0 after a write.
0042  */
0043 static inline size_t w1_f0d_fix_count(loff_t off, size_t count, size_t size)
0044 {
0045     if (off > size)
0046         return 0;
0047 
0048     if ((off + count) > size)
0049         return size - off;
0050 
0051     return count;
0052 }
0053 
0054 /*
0055  * Read a block from W1 ROM two times and compares the results.
0056  * If they are equal they are returned, otherwise the read
0057  * is repeated W1_F0D_READ_RETRIES times.
0058  *
0059  * count must not exceed W1_F0D_READ_MAXLEN.
0060  */
0061 static int w1_f0d_readblock(struct w1_slave *sl, int off, int count, char *buf)
0062 {
0063     u8 wrbuf[3];
0064     u8 cmp[W1_F0D_READ_MAXLEN];
0065     int tries = W1_F0D_READ_RETRIES;
0066 
0067     do {
0068         wrbuf[0] = W1_F0D_READ_EEPROM;
0069         wrbuf[1] = off & 0x7f;
0070         wrbuf[2] = 0;
0071 
0072         if (w1_reset_select_slave(sl))
0073             return -1;
0074 
0075         w1_write_block(sl->master, wrbuf, sizeof(wrbuf));
0076         w1_read_block(sl->master, buf, count);
0077 
0078         if (w1_reset_select_slave(sl))
0079             return -1;
0080 
0081         w1_write_block(sl->master, wrbuf, sizeof(wrbuf));
0082         w1_read_block(sl->master, cmp, count);
0083 
0084         if (!memcmp(cmp, buf, count))
0085             return 0;
0086     } while (--tries);
0087 
0088     dev_err(&sl->dev, "proof reading failed %d times\n",
0089             W1_F0D_READ_RETRIES);
0090 
0091     return -1;
0092 }
0093 
0094 static ssize_t w1_f0d_read_bin(struct file *filp, struct kobject *kobj,
0095                    struct bin_attribute *bin_attr,
0096                    char *buf, loff_t off, size_t count)
0097 {
0098     struct w1_slave *sl = kobj_to_w1_slave(kobj);
0099     int todo = count;
0100 
0101     count = w1_f0d_fix_count(off, count, W1_F0D_EEPROM_SIZE);
0102     if (count == 0)
0103         return 0;
0104 
0105     mutex_lock(&sl->master->mutex);
0106 
0107     /* read directly from the EEPROM in chunks of W1_F0D_READ_MAXLEN */
0108     while (todo > 0) {
0109         int block_read;
0110 
0111         if (todo >= W1_F0D_READ_MAXLEN)
0112             block_read = W1_F0D_READ_MAXLEN;
0113         else
0114             block_read = todo;
0115 
0116         if (w1_f0d_readblock(sl, off, block_read, buf) < 0) {
0117             count = -EIO;
0118             break;
0119         }
0120 
0121         todo -= W1_F0D_READ_MAXLEN;
0122         buf += W1_F0D_READ_MAXLEN;
0123         off += W1_F0D_READ_MAXLEN;
0124     }
0125 
0126     mutex_unlock(&sl->master->mutex);
0127 
0128     return count;
0129 }
0130 
0131 /*
0132  * Writes to the scratchpad and reads it back for verification.
0133  * Then copies the scratchpad to EEPROM.
0134  * The data must be aligned at W1_F0D_SCRATCH_SIZE bytes and
0135  * must be W1_F0D_SCRATCH_SIZE bytes long.
0136  * The master must be locked.
0137  *
0138  * @param sl    The slave structure
0139  * @param addr  Address for the write
0140  * @param len   length must be <= (W1_F0D_PAGE_SIZE - (addr & W1_F0D_PAGE_MASK))
0141  * @param data  The data to write
0142  * @return  0=Success -1=failure
0143  */
0144 static int w1_f0d_write(struct w1_slave *sl, int addr, int len, const u8 *data)
0145 {
0146     int tries = W1_F0D_READ_RETRIES;
0147     u8 wrbuf[3];
0148     u8 rdbuf[W1_F0D_SCRATCH_SIZE];
0149     u8 cs;
0150 
0151     if ((addr & 1) || (len != 2)) {
0152         dev_err(&sl->dev, "%s: bad addr/len -  addr=%#x len=%d\n",
0153             __func__, addr, len);
0154         return -1;
0155     }
0156 
0157 retry:
0158 
0159     /* Write the data to the scratchpad */
0160     if (w1_reset_select_slave(sl))
0161         return -1;
0162 
0163     wrbuf[0] = W1_F0D_WRITE_EEPROM;
0164     wrbuf[1] = addr & 0xff;
0165     wrbuf[2] = 0xff; /* ?? from Example */
0166 
0167     w1_write_block(sl->master, wrbuf, sizeof(wrbuf));
0168     w1_write_block(sl->master, data, len);
0169 
0170     w1_read_block(sl->master, rdbuf, sizeof(rdbuf));
0171     /* Compare what was read against the data written */
0172     if ((rdbuf[0] != data[0]) || (rdbuf[1] != data[1])) {
0173 
0174         if (--tries)
0175             goto retry;
0176 
0177         dev_err(&sl->dev,
0178             "could not write to eeprom, scratchpad compare failed %d times\n",
0179             W1_F0D_READ_RETRIES);
0180         pr_info("%s: rdbuf = %#x %#x data = %#x %#x\n",
0181             __func__, rdbuf[0], rdbuf[1], data[0], data[1]);
0182 
0183         return -1;
0184     }
0185 
0186     /* Trigger write out to EEPROM */
0187     w1_write_8(sl->master, W1_F0D_RELEASE);
0188 
0189     /* Sleep for tprog ms to wait for the write to complete */
0190     msleep(W1_F0D_TPROG_MS);
0191 
0192     /* Check CS (Command Status) == 0xAA ? */
0193     cs = w1_read_8(sl->master);
0194     if (cs != W1_F0D_CS_OK) {
0195         dev_err(&sl->dev, "save to eeprom failed = CS=%#x\n", cs);
0196         return -1;
0197     }
0198 
0199     return 0;
0200 }
0201 
0202 static ssize_t w1_f0d_write_bin(struct file *filp, struct kobject *kobj,
0203                 struct bin_attribute *bin_attr,
0204                 char *buf, loff_t off, size_t count)
0205 {
0206     struct w1_slave *sl = kobj_to_w1_slave(kobj);
0207     int addr, len;
0208     int copy;
0209 
0210     count = w1_f0d_fix_count(off, count, W1_F0D_EEPROM_SIZE);
0211     if (count == 0)
0212         return 0;
0213 
0214     mutex_lock(&sl->master->mutex);
0215 
0216     /* Can only write data in blocks of the size of the scratchpad */
0217     addr = off;
0218     len = count;
0219     while (len > 0) {
0220 
0221         /* if len too short or addr not aligned */
0222         if (len < W1_F0D_SCRATCH_SIZE || addr & W1_F0D_SCRATCH_MASK) {
0223             char tmp[W1_F0D_SCRATCH_SIZE];
0224 
0225             /* read the block and update the parts to be written */
0226             if (w1_f0d_readblock(sl, addr & ~W1_F0D_SCRATCH_MASK,
0227                     W1_F0D_SCRATCH_SIZE, tmp)) {
0228                 count = -EIO;
0229                 goto out_up;
0230             }
0231 
0232             /* copy at most to the boundary of the PAGE or len */
0233             copy = W1_F0D_SCRATCH_SIZE -
0234                 (addr & W1_F0D_SCRATCH_MASK);
0235 
0236             if (copy > len)
0237                 copy = len;
0238 
0239             memcpy(&tmp[addr & W1_F0D_SCRATCH_MASK], buf, copy);
0240             if (w1_f0d_write(sl, addr & ~W1_F0D_SCRATCH_MASK,
0241                     W1_F0D_SCRATCH_SIZE, tmp) < 0) {
0242                 count = -EIO;
0243                 goto out_up;
0244             }
0245         } else {
0246 
0247             copy = W1_F0D_SCRATCH_SIZE;
0248             if (w1_f0d_write(sl, addr, copy, buf) < 0) {
0249                 count = -EIO;
0250                 goto out_up;
0251             }
0252         }
0253         buf += copy;
0254         addr += copy;
0255         len -= copy;
0256     }
0257 
0258 out_up:
0259     mutex_unlock(&sl->master->mutex);
0260 
0261     return count;
0262 }
0263 
0264 static struct bin_attribute w1_f0d_bin_attr = {
0265     .attr = {
0266         .name = "eeprom",
0267         .mode = S_IRUGO | S_IWUSR,
0268     },
0269     .size = W1_F0D_EEPROM_SIZE,
0270     .read = w1_f0d_read_bin,
0271     .write = w1_f0d_write_bin,
0272 };
0273 
0274 static int w1_f0d_add_slave(struct w1_slave *sl)
0275 {
0276     return sysfs_create_bin_file(&sl->dev.kobj, &w1_f0d_bin_attr);
0277 }
0278 
0279 static void w1_f0d_remove_slave(struct w1_slave *sl)
0280 {
0281     sysfs_remove_bin_file(&sl->dev.kobj, &w1_f0d_bin_attr);
0282 }
0283 
0284 static const struct w1_family_ops w1_f0d_fops = {
0285     .add_slave      = w1_f0d_add_slave,
0286     .remove_slave   = w1_f0d_remove_slave,
0287 };
0288 
0289 static struct w1_family w1_family_0d = {
0290     .fid = W1_EEPROM_DS2805,
0291     .fops = &w1_f0d_fops,
0292 };
0293 
0294 module_w1_family(w1_family_0d);
0295 
0296 MODULE_LICENSE("GPL");
0297 MODULE_AUTHOR("Andrew Worsley amworsley@gmail.com");
0298 MODULE_DESCRIPTION("w1 family 0d driver for DS2805, 1kb EEPROM");