295 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * w1_ds2431.c - w1 family 2d (DS2431) driver4 *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 0x2D20 21#define W1_F2D_EEPROM_SIZE 12822#define W1_F2D_PAGE_COUNT 423#define W1_F2D_PAGE_BITS 524#define W1_F2D_PAGE_SIZE (1<<W1_F2D_PAGE_BITS)25#define W1_F2D_PAGE_MASK 0x1F26 27#define W1_F2D_SCRATCH_BITS 328#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 0xF032#define W1_F2D_WRITE_SCRATCH 0x0F33#define W1_F2D_READ_SCRATCH 0xAA34#define W1_F2D_COPY_SCRATCH 0x5535 36 37#define W1_F2D_TPROG_MS 1138 39#define W1_F2D_READ_RETRIES 1040#define W1_F2D_READ_MAXLEN 841 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 read60 * 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 else117 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 and136 * must be W1_F2D_SCRATCH_SIZE bytes long.137 * The master must be locked.138 *139 * @param sl The slave structure140 * @param addr Address for the write141 * @param len length must be <= (W1_F2D_PAGE_SIZE - (addr & W1_F2D_PAGE_MASK))142 * @param data The data to write143 * @return 0=Success -1=failure144 */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 const 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