brintos

brintos / linux-shallow public Read only

0
0
Text · 9.4 KiB · 2854b8b Raw
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