w1_ds2433.c 7.1 KB
Newer Older
1 2 3 4 5
/*
 *	w1_ds2433.c - w1 family 23 (DS2433) driver
 *
 * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com>
 *
6 7
 * This source code is licensed under the GNU General Public License,
 * Version 2. See the file COPYING for more details.
8 9 10 11 12 13 14 15
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <linux/types.h>
#include <linux/delay.h>
16
#ifdef CONFIG_W1_SLAVE_DS2433_CRC
17
#include <linux/crc16.h>
18 19 20 21

#define CRC16_INIT		0
#define CRC16_VALID		0xb001

22
#endif
23

24 25 26
#include "../w1.h"
#include "../w1_int.h"
#include "../w1_family.h"
27 28 29 30 31 32

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>");
MODULE_DESCRIPTION("w1 family 23 driver for DS2433, 4kb EEPROM");

#define W1_EEPROM_SIZE		512
33
#define W1_PAGE_COUNT		16
34 35 36 37
#define W1_PAGE_SIZE		32
#define W1_PAGE_BITS		5
#define W1_PAGE_MASK		0x1F

38 39
#define W1_F23_TIME		300

40 41 42 43 44
#define W1_F23_READ_EEPROM	0xF0
#define W1_F23_WRITE_SCRATCH	0x0F
#define W1_F23_READ_SCRATCH	0xAA
#define W1_F23_COPY_SCRATCH	0x55

45 46 47 48 49
struct w1_f23_data {
	u8	memory[W1_EEPROM_SIZE];
	u32	validcrc;
};

50 51
/**
 * Check the file size bounds and adjusts count as needed.
52
 * This would not be needed if the file size didn't reset to 0 after a write.
53 54 55 56 57 58 59 60 61 62 63 64
 */
static inline size_t w1_f23_fix_count(loff_t off, size_t count, size_t size)
{
	if (off > size)
		return 0;

	if ((off + count) > size)
		return (size - off);

	return count;
}

65
#ifdef CONFIG_W1_SLAVE_DS2433_CRC
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
static int w1_f23_refresh_block(struct w1_slave *sl, struct w1_f23_data *data,
				int block)
{
	u8	wrbuf[3];
	int	off = block * W1_PAGE_SIZE;

	if (data->validcrc & (1 << block))
		return 0;

	if (w1_reset_select_slave(sl)) {
		data->validcrc = 0;
		return -EIO;
	}

	wrbuf[0] = W1_F23_READ_EEPROM;
	wrbuf[1] = off & 0xff;
	wrbuf[2] = off >> 8;
	w1_write_block(sl->master, wrbuf, 3);
	w1_read_block(sl->master, &data->memory[off], W1_PAGE_SIZE);

	/* cache the block if the CRC is valid */
	if (crc16(CRC16_INIT, &data->memory[off], W1_PAGE_SIZE) == CRC16_VALID)
		data->validcrc |= (1 << block);

	return 0;
}
92
#endif	/* CONFIG_W1_SLAVE_DS2433_CRC */
93 94 95

static ssize_t w1_f23_read_bin(struct kobject *kobj, char *buf, loff_t off,
			       size_t count)
96 97
{
	struct w1_slave *sl = kobj_to_w1_slave(kobj);
98
#ifdef CONFIG_W1_SLAVE_DS2433_CRC
99 100 101
	struct w1_f23_data *data = sl->family_data;
	int i, min_page, max_page;
#else
102
	u8 wrbuf[3];
103
#endif
104 105 106 107

	if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0)
		return 0;

108
	mutex_lock(&sl->master->mutex);
109

110
#ifdef CONFIG_W1_SLAVE_DS2433_CRC
111 112 113 114 115 116 117 118 119 120 121

	min_page = (off >> W1_PAGE_BITS);
	max_page = (off + count - 1) >> W1_PAGE_BITS;
	for (i = min_page; i <= max_page; i++) {
		if (w1_f23_refresh_block(sl, data, i)) {
			count = -EIO;
			goto out_up;
		}
	}
	memcpy(buf, &data->memory[off], count);

122
#else 	/* CONFIG_W1_SLAVE_DS2433_CRC */
123

124 125 126 127 128 129 130 131 132 133 134 135
	/* read directly from the EEPROM */
	if (w1_reset_select_slave(sl)) {
		count = -EIO;
		goto out_up;
	}

	wrbuf[0] = W1_F23_READ_EEPROM;
	wrbuf[1] = off & 0xff;
	wrbuf[2] = off >> 8;
	w1_write_block(sl->master, wrbuf, 3);
	w1_read_block(sl->master, buf, count);

136
#endif	/* CONFIG_W1_SLAVE_DS2433_CRC */
137

138
out_up:
139
	mutex_unlock(&sl->master->mutex);
140 141 142 143 144 145

	return count;
}

/**
 * Writes to the scratchpad and reads it back for verification.
146 147
 * Then copies the scratchpad to EEPROM.
 * The data must be on one page.
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
 * The master must be locked.
 *
 * @param sl	The slave structure
 * @param addr	Address for the write
 * @param len   length must be <= (W1_PAGE_SIZE - (addr & W1_PAGE_MASK))
 * @param data	The data to write
 * @return	0=Success -1=failure
 */
static int w1_f23_write(struct w1_slave *sl, int addr, int len, const u8 *data)
{
	u8 wrbuf[4];
	u8 rdbuf[W1_PAGE_SIZE + 3];
	u8 es = (addr + len - 1) & 0x1f;

	/* Write the data to the scratchpad */
	if (w1_reset_select_slave(sl))
		return -1;

	wrbuf[0] = W1_F23_WRITE_SCRATCH;
	wrbuf[1] = addr & 0xff;
	wrbuf[2] = addr >> 8;

	w1_write_block(sl->master, wrbuf, 3);
	w1_write_block(sl->master, data, len);

	/* Read the scratchpad and verify */
	if (w1_reset_select_slave(sl))
		return -1;

	w1_write_8(sl->master, W1_F23_READ_SCRATCH);
	w1_read_block(sl->master, rdbuf, len + 3);

	/* Compare what was read against the data written */
	if ((rdbuf[0] != wrbuf[1]) || (rdbuf[1] != wrbuf[2]) ||
	    (rdbuf[2] != es) || (memcmp(data, &rdbuf[3], len) != 0))
		return -1;

	/* Copy the scratchpad to EEPROM */
	if (w1_reset_select_slave(sl))
		return -1;

	wrbuf[0] = W1_F23_COPY_SCRATCH;
	wrbuf[3] = es;
	w1_write_block(sl->master, wrbuf, 4);

	/* Sleep for 5 ms to wait for the write to complete */
	msleep(5);

	/* Reset the bus to wake up the EEPROM (this may not be needed) */
	w1_reset_bus(sl->master);

	return 0;
}

static ssize_t w1_f23_write_bin(struct kobject *kobj, char *buf, loff_t off,
				size_t count)
{
	struct w1_slave *sl = kobj_to_w1_slave(kobj);
	int addr, len, idx;

	if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0)
		return 0;

211
#ifdef CONFIG_W1_SLAVE_DS2433_CRC
212 213
	/* can only write full blocks in cached mode */
	if ((off & W1_PAGE_MASK) || (count & W1_PAGE_MASK)) {
214
		dev_err(&sl->dev, "invalid offset/count off=%d cnt=%zd\n",
215 216 217 218 219 220 221 222 223 224 225
			(int)off, count);
		return -EINVAL;
	}

	/* make sure the block CRCs are valid */
	for (idx = 0; idx < count; idx += W1_PAGE_SIZE) {
		if (crc16(CRC16_INIT, &buf[idx], W1_PAGE_SIZE) != CRC16_VALID) {
			dev_err(&sl->dev, "bad CRC at offset %d\n", (int)off);
			return -EINVAL;
		}
	}
226
#endif	/* CONFIG_W1_SLAVE_DS2433_CRC */
227

228
	mutex_lock(&sl->master->mutex);
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245

	/* Can only write data to one page at a time */
	idx = 0;
	while (idx < count) {
		addr = off + idx;
		len = W1_PAGE_SIZE - (addr & W1_PAGE_MASK);
		if (len > (count - idx))
			len = count - idx;

		if (w1_f23_write(sl, addr, len, &buf[idx]) < 0) {
			count = -EIO;
			goto out_up;
		}
		idx += len;
	}

out_up:
246
	mutex_unlock(&sl->master->mutex);
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262

	return count;
}

static struct bin_attribute w1_f23_bin_attr = {
	.attr = {
		.name = "eeprom",
		.mode = S_IRUGO | S_IWUSR,
	},
	.size = W1_EEPROM_SIZE,
	.read = w1_f23_read_bin,
	.write = w1_f23_write_bin,
};

static int w1_f23_add_slave(struct w1_slave *sl)
{
263
	int err;
264
#ifdef CONFIG_W1_SLAVE_DS2433_CRC
265 266 267 268 269 270 271 272
	struct w1_f23_data *data;

	data = kmalloc(sizeof(struct w1_f23_data), GFP_KERNEL);
	if (!data)
		return -ENOMEM;
	memset(data, 0, sizeof(struct w1_f23_data));
	sl->family_data = data;

273
#endif	/* CONFIG_W1_SLAVE_DS2433_CRC */
274 275 276

	err = sysfs_create_bin_file(&sl->dev.kobj, &w1_f23_bin_attr);

277
#ifdef CONFIG_W1_SLAVE_DS2433_CRC
278 279
	if (err)
		kfree(data);
280
#endif	/* CONFIG_W1_SLAVE_DS2433_CRC */
281 282

	return err;
283 284 285 286
}

static void w1_f23_remove_slave(struct w1_slave *sl)
{
287
#ifdef CONFIG_W1_SLAVE_DS2433_CRC
288 289
	kfree(sl->family_data);
	sl->family_data = NULL;
290
#endif	/* CONFIG_W1_SLAVE_DS2433_CRC */
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
	sysfs_remove_bin_file(&sl->dev.kobj, &w1_f23_bin_attr);
}

static struct w1_family_ops w1_f23_fops = {
	.add_slave      = w1_f23_add_slave,
	.remove_slave   = w1_f23_remove_slave,
};

static struct w1_family w1_family_23 = {
	.fid = W1_EEPROM_DS2433,
	.fops = &w1_f23_fops,
};

static int __init w1_f23_init(void)
{
	return w1_register_family(&w1_family_23);
}

static void __exit w1_f23_fini(void)
{
	w1_unregister_family(&w1_family_23);
}

module_init(w1_f23_init);
module_exit(w1_f23_fini);