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

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

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

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

	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);

123
#else 	/* CONFIG_W1_SLAVE_DS2433_CRC */
124

125 126 127 128 129 130 131 132 133 134 135 136
	/* 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);

137
#endif	/* CONFIG_W1_SLAVE_DS2433_CRC */
138

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

	return count;
}

/**
 * Writes to the scratchpad and reads it back for verification.
147 148
 * Then copies the scratchpad to EEPROM.
 * The data must be on one page.
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
 * 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;
}

203 204 205
static ssize_t w1_f23_write_bin(struct kobject *kobj,
				struct bin_attribute *bin_attr,
				char *buf, loff_t off, size_t count)
206 207 208 209 210 211 212
{
	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;

213
#ifdef CONFIG_W1_SLAVE_DS2433_CRC
214 215
	/* can only write full blocks in cached mode */
	if ((off & W1_PAGE_MASK) || (count & W1_PAGE_MASK)) {
216
		dev_err(&sl->dev, "invalid offset/count off=%d cnt=%zd\n",
217 218 219 220 221 222 223 224 225 226 227
			(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;
		}
	}
228
#endif	/* CONFIG_W1_SLAVE_DS2433_CRC */
229

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

	/* 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:
248
	mutex_unlock(&sl->master->mutex);
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264

	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)
{
265
	int err;
266
#ifdef CONFIG_W1_SLAVE_DS2433_CRC
267 268 269 270 271 272 273 274
	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;

275
#endif	/* CONFIG_W1_SLAVE_DS2433_CRC */
276 277 278

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

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

	return err;
285 286 287 288
}

static void w1_f23_remove_slave(struct w1_slave *sl)
{
289
#ifdef CONFIG_W1_SLAVE_DS2433_CRC
290 291
	kfree(sl->family_data);
	sl->family_data = NULL;
292
#endif	/* CONFIG_W1_SLAVE_DS2433_CRC */
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
	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);