418 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * w1_ds28e04.c - w1 family 1C (DS28E04) driver4 *5 * Copyright (c) 2012 Markus Franke <franke.m@sebakmt.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#include <linux/slab.h>15#include <linux/crc16.h>16#include <linux/uaccess.h>17 18#define CRC16_INIT 019#define CRC16_VALID 0xb00120 21#include <linux/w1.h>22 23#define W1_FAMILY_DS28E04 0x1C24 25/* Allow the strong pullup to be disabled, but default to enabled.26 * If it was disabled a parasite powered device might not get the required27 * current to copy the data from the scratchpad to EEPROM. If it is enabled28 * parasite powered devices have a better chance of getting the current29 * required.30 */31static int w1_strong_pullup = 1;32module_param_named(strong_pullup, w1_strong_pullup, int, 0);33 34/* enable/disable CRC checking on DS28E04-100 memory accesses */35static bool w1_enable_crccheck = true;36 37#define W1_EEPROM_SIZE 51238#define W1_PAGE_COUNT 1639#define W1_PAGE_SIZE 3240#define W1_PAGE_BITS 541#define W1_PAGE_MASK 0x1F42 43#define W1_F1C_READ_EEPROM 0xF044#define W1_F1C_WRITE_SCRATCH 0x0F45#define W1_F1C_READ_SCRATCH 0xAA46#define W1_F1C_COPY_SCRATCH 0x5547#define W1_F1C_ACCESS_WRITE 0x5A48 49#define W1_1C_REG_LOGIC_STATE 0x22050 51struct w1_f1C_data {52 u8 memory[W1_EEPROM_SIZE];53 u32 validcrc;54};55 56/*57 * Check the file size bounds and adjusts count as needed.58 * This would not be needed if the file size didn't reset to 0 after a write.59 */60static inline size_t w1_f1C_fix_count(loff_t off, size_t count, size_t size)61{62 if (off > size)63 return 0;64 65 if ((off + count) > size)66 return size - off;67 68 return count;69}70 71static int w1_f1C_refresh_block(struct w1_slave *sl, struct w1_f1C_data *data,72 int block)73{74 u8 wrbuf[3];75 int off = block * W1_PAGE_SIZE;76 77 if (data->validcrc & (1 << block))78 return 0;79 80 if (w1_reset_select_slave(sl)) {81 data->validcrc = 0;82 return -EIO;83 }84 85 wrbuf[0] = W1_F1C_READ_EEPROM;86 wrbuf[1] = off & 0xff;87 wrbuf[2] = off >> 8;88 w1_write_block(sl->master, wrbuf, 3);89 w1_read_block(sl->master, &data->memory[off], W1_PAGE_SIZE);90 91 /* cache the block if the CRC is valid */92 if (crc16(CRC16_INIT, &data->memory[off], W1_PAGE_SIZE) == CRC16_VALID)93 data->validcrc |= (1 << block);94 95 return 0;96}97 98static int w1_f1C_read(struct w1_slave *sl, int addr, int len, char *data)99{100 u8 wrbuf[3];101 102 /* read directly from the EEPROM */103 if (w1_reset_select_slave(sl))104 return -EIO;105 106 wrbuf[0] = W1_F1C_READ_EEPROM;107 wrbuf[1] = addr & 0xff;108 wrbuf[2] = addr >> 8;109 110 w1_write_block(sl->master, wrbuf, sizeof(wrbuf));111 return w1_read_block(sl->master, data, len);112}113 114static ssize_t eeprom_read(struct file *filp, struct kobject *kobj,115 struct bin_attribute *bin_attr, char *buf,116 loff_t off, size_t count)117{118 struct w1_slave *sl = kobj_to_w1_slave(kobj);119 struct w1_f1C_data *data = sl->family_data;120 int i, min_page, max_page;121 122 count = w1_f1C_fix_count(off, count, W1_EEPROM_SIZE);123 if (count == 0)124 return 0;125 126 mutex_lock(&sl->master->mutex);127 128 if (w1_enable_crccheck) {129 min_page = (off >> W1_PAGE_BITS);130 max_page = (off + count - 1) >> W1_PAGE_BITS;131 for (i = min_page; i <= max_page; i++) {132 if (w1_f1C_refresh_block(sl, data, i)) {133 count = -EIO;134 goto out_up;135 }136 }137 memcpy(buf, &data->memory[off], count);138 } else {139 count = w1_f1C_read(sl, off, count, buf);140 }141 142out_up:143 mutex_unlock(&sl->master->mutex);144 145 return count;146}147 148/**149 * w1_f1C_write() - Writes to the scratchpad and reads it back for verification.150 * @sl: The slave structure151 * @addr: Address for the write152 * @len: length must be <= (W1_PAGE_SIZE - (addr & W1_PAGE_MASK))153 * @data: The data to write154 *155 * Then copies the scratchpad to EEPROM.156 * The data must be on one page.157 * The master must be locked.158 *159 * Return: 0=Success, -1=failure160 */161static int w1_f1C_write(struct w1_slave *sl, int addr, int len, const u8 *data)162{163 u8 wrbuf[4];164 u8 rdbuf[W1_PAGE_SIZE + 3];165 u8 es = (addr + len - 1) & 0x1f;166 unsigned int tm = 10;167 int i;168 struct w1_f1C_data *f1C = sl->family_data;169 170 /* Write the data to the scratchpad */171 if (w1_reset_select_slave(sl))172 return -1;173 174 wrbuf[0] = W1_F1C_WRITE_SCRATCH;175 wrbuf[1] = addr & 0xff;176 wrbuf[2] = addr >> 8;177 178 w1_write_block(sl->master, wrbuf, 3);179 w1_write_block(sl->master, data, len);180 181 /* Read the scratchpad and verify */182 if (w1_reset_select_slave(sl))183 return -1;184 185 w1_write_8(sl->master, W1_F1C_READ_SCRATCH);186 w1_read_block(sl->master, rdbuf, len + 3);187 188 /* Compare what was read against the data written */189 if ((rdbuf[0] != wrbuf[1]) || (rdbuf[1] != wrbuf[2]) ||190 (rdbuf[2] != es) || (memcmp(data, &rdbuf[3], len) != 0))191 return -1;192 193 /* Copy the scratchpad to EEPROM */194 if (w1_reset_select_slave(sl))195 return -1;196 197 wrbuf[0] = W1_F1C_COPY_SCRATCH;198 wrbuf[3] = es;199 200 for (i = 0; i < sizeof(wrbuf); ++i) {201 /*202 * issue 10ms strong pullup (or delay) on the last byte203 * for writing the data from the scratchpad to EEPROM204 */205 if (w1_strong_pullup && i == sizeof(wrbuf)-1)206 w1_next_pullup(sl->master, tm);207 208 w1_write_8(sl->master, wrbuf[i]);209 }210 211 if (!w1_strong_pullup)212 msleep(tm);213 214 if (w1_enable_crccheck) {215 /* invalidate cached data */216 f1C->validcrc &= ~(1 << (addr >> W1_PAGE_BITS));217 }218 219 /* Reset the bus to wake up the EEPROM (this may not be needed) */220 w1_reset_bus(sl->master);221 222 return 0;223}224 225static ssize_t eeprom_write(struct file *filp, struct kobject *kobj,226 struct bin_attribute *bin_attr, char *buf,227 loff_t off, size_t count)228 229{230 struct w1_slave *sl = kobj_to_w1_slave(kobj);231 int addr, len, idx;232 233 count = w1_f1C_fix_count(off, count, W1_EEPROM_SIZE);234 if (count == 0)235 return 0;236 237 if (w1_enable_crccheck) {238 /* can only write full blocks in cached mode */239 if ((off & W1_PAGE_MASK) || (count & W1_PAGE_MASK)) {240 dev_err(&sl->dev, "invalid offset/count off=%d cnt=%zd\n",241 (int)off, count);242 return -EINVAL;243 }244 245 /* make sure the block CRCs are valid */246 for (idx = 0; idx < count; idx += W1_PAGE_SIZE) {247 if (crc16(CRC16_INIT, &buf[idx], W1_PAGE_SIZE)248 != CRC16_VALID) {249 dev_err(&sl->dev, "bad CRC at offset %d\n",250 (int)off);251 return -EINVAL;252 }253 }254 }255 256 mutex_lock(&sl->master->mutex);257 258 /* Can only write data to one page at a time */259 idx = 0;260 while (idx < count) {261 addr = off + idx;262 len = W1_PAGE_SIZE - (addr & W1_PAGE_MASK);263 if (len > (count - idx))264 len = count - idx;265 266 if (w1_f1C_write(sl, addr, len, &buf[idx]) < 0) {267 count = -EIO;268 goto out_up;269 }270 idx += len;271 }272 273out_up:274 mutex_unlock(&sl->master->mutex);275 276 return count;277}278 279static BIN_ATTR_RW(eeprom, W1_EEPROM_SIZE);280 281static ssize_t pio_read(struct file *filp, struct kobject *kobj,282 struct bin_attribute *bin_attr, char *buf, loff_t off,283 size_t count)284 285{286 struct w1_slave *sl = kobj_to_w1_slave(kobj);287 int ret;288 289 /* check arguments */290 if (off != 0 || count != 1 || buf == NULL)291 return -EINVAL;292 293 mutex_lock(&sl->master->mutex);294 ret = w1_f1C_read(sl, W1_1C_REG_LOGIC_STATE, count, buf);295 mutex_unlock(&sl->master->mutex);296 297 return ret;298}299 300static ssize_t pio_write(struct file *filp, struct kobject *kobj,301 struct bin_attribute *bin_attr, char *buf, loff_t off,302 size_t count)303 304{305 struct w1_slave *sl = kobj_to_w1_slave(kobj);306 u8 wrbuf[3];307 u8 ack;308 309 /* check arguments */310 if (off != 0 || count != 1 || buf == NULL)311 return -EINVAL;312 313 mutex_lock(&sl->master->mutex);314 315 /* Write the PIO data */316 if (w1_reset_select_slave(sl)) {317 mutex_unlock(&sl->master->mutex);318 return -1;319 }320 321 /* set bit 7..2 to value '1' */322 *buf = *buf | 0xFC;323 324 wrbuf[0] = W1_F1C_ACCESS_WRITE;325 wrbuf[1] = *buf;326 wrbuf[2] = ~(*buf);327 w1_write_block(sl->master, wrbuf, 3);328 329 w1_read_block(sl->master, &ack, sizeof(ack));330 331 mutex_unlock(&sl->master->mutex);332 333 /* check for acknowledgement */334 if (ack != 0xAA)335 return -EIO;336 337 return count;338}339 340static BIN_ATTR_RW(pio, 1);341 342static ssize_t crccheck_show(struct device *dev, struct device_attribute *attr,343 char *buf)344{345 return sysfs_emit(buf, "%d\n", w1_enable_crccheck);346}347 348static ssize_t crccheck_store(struct device *dev, struct device_attribute *attr,349 const char *buf, size_t count)350{351 int err = kstrtobool(buf, &w1_enable_crccheck);352 353 if (err)354 return err;355 356 return count;357}358 359static DEVICE_ATTR_RW(crccheck);360 361static struct attribute *w1_f1C_attrs[] = {362 &dev_attr_crccheck.attr,363 NULL,364};365 366static struct bin_attribute *w1_f1C_bin_attrs[] = {367 &bin_attr_eeprom,368 &bin_attr_pio,369 NULL,370};371 372static const struct attribute_group w1_f1C_group = {373 .attrs = w1_f1C_attrs,374 .bin_attrs = w1_f1C_bin_attrs,375};376 377static const struct attribute_group *w1_f1C_groups[] = {378 &w1_f1C_group,379 NULL,380};381 382static int w1_f1C_add_slave(struct w1_slave *sl)383{384 struct w1_f1C_data *data = NULL;385 386 if (w1_enable_crccheck) {387 data = kzalloc(sizeof(struct w1_f1C_data), GFP_KERNEL);388 if (!data)389 return -ENOMEM;390 sl->family_data = data;391 }392 393 return 0;394}395 396static void w1_f1C_remove_slave(struct w1_slave *sl)397{398 kfree(sl->family_data);399 sl->family_data = NULL;400}401 402static const struct w1_family_ops w1_f1C_fops = {403 .add_slave = w1_f1C_add_slave,404 .remove_slave = w1_f1C_remove_slave,405 .groups = w1_f1C_groups,406};407 408static struct w1_family w1_family_1C = {409 .fid = W1_FAMILY_DS28E04,410 .fops = &w1_f1C_fops,411};412module_w1_family(w1_family_1C);413 414MODULE_AUTHOR("Markus Franke <franke.m@sebakmt.com>, <franm@hrz.tu-chemnitz.de>");415MODULE_DESCRIPTION("w1 family 1C driver for DS28E04, 4kb EEPROM and PIO");416MODULE_LICENSE("GPL");417MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS28E04));418