1bd529cfbSEvgeniy Polyakov /* 2bd529cfbSEvgeniy Polyakov * w1_ds2433.c - w1 family 23 (DS2433) driver 3bd529cfbSEvgeniy Polyakov * 4bd529cfbSEvgeniy Polyakov * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com> 5bd529cfbSEvgeniy Polyakov * 6bd529cfbSEvgeniy Polyakov * This source code is licensed under the GNU General Public License, 7bd529cfbSEvgeniy Polyakov * Version 2. See the file COPYING for more details. 8bd529cfbSEvgeniy Polyakov */ 9bd529cfbSEvgeniy Polyakov 10bd529cfbSEvgeniy Polyakov #include <linux/kernel.h> 11bd529cfbSEvgeniy Polyakov #include <linux/module.h> 12bd529cfbSEvgeniy Polyakov #include <linux/moduleparam.h> 13bd529cfbSEvgeniy Polyakov #include <linux/device.h> 14bd529cfbSEvgeniy Polyakov #include <linux/types.h> 15bd529cfbSEvgeniy Polyakov #include <linux/delay.h> 16e9d55f9dSEvgeniy Polyakov #ifdef CONFIG_W1_SLAVE_DS2433_CRC 17bd529cfbSEvgeniy Polyakov #include <linux/crc16.h> 18bd529cfbSEvgeniy Polyakov 19bd529cfbSEvgeniy Polyakov #define CRC16_INIT 0 20bd529cfbSEvgeniy Polyakov #define CRC16_VALID 0xb001 21bd529cfbSEvgeniy Polyakov 22bd529cfbSEvgeniy Polyakov #endif 23bd529cfbSEvgeniy Polyakov 24bd529cfbSEvgeniy Polyakov #include "../w1.h" 25bd529cfbSEvgeniy Polyakov #include "../w1_int.h" 26bd529cfbSEvgeniy Polyakov #include "../w1_family.h" 27bd529cfbSEvgeniy Polyakov 28bd529cfbSEvgeniy Polyakov MODULE_LICENSE("GPL"); 29bd529cfbSEvgeniy Polyakov MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>"); 30bd529cfbSEvgeniy Polyakov MODULE_DESCRIPTION("w1 family 23 driver for DS2433, 4kb EEPROM"); 31bd529cfbSEvgeniy Polyakov 32bd529cfbSEvgeniy Polyakov #define W1_EEPROM_SIZE 512 33bd529cfbSEvgeniy Polyakov #define W1_PAGE_COUNT 16 34bd529cfbSEvgeniy Polyakov #define W1_PAGE_SIZE 32 35bd529cfbSEvgeniy Polyakov #define W1_PAGE_BITS 5 36bd529cfbSEvgeniy Polyakov #define W1_PAGE_MASK 0x1F 37bd529cfbSEvgeniy Polyakov 38bd529cfbSEvgeniy Polyakov #define W1_F23_TIME 300 39bd529cfbSEvgeniy Polyakov 40bd529cfbSEvgeniy Polyakov #define W1_F23_READ_EEPROM 0xF0 41bd529cfbSEvgeniy Polyakov #define W1_F23_WRITE_SCRATCH 0x0F 42bd529cfbSEvgeniy Polyakov #define W1_F23_READ_SCRATCH 0xAA 43bd529cfbSEvgeniy Polyakov #define W1_F23_COPY_SCRATCH 0x55 44bd529cfbSEvgeniy Polyakov 45bd529cfbSEvgeniy Polyakov struct w1_f23_data { 46bd529cfbSEvgeniy Polyakov u8 memory[W1_EEPROM_SIZE]; 47bd529cfbSEvgeniy Polyakov u32 validcrc; 48bd529cfbSEvgeniy Polyakov }; 49bd529cfbSEvgeniy Polyakov 50bd529cfbSEvgeniy Polyakov /** 51bd529cfbSEvgeniy Polyakov * Check the file size bounds and adjusts count as needed. 52bd529cfbSEvgeniy Polyakov * This would not be needed if the file size didn't reset to 0 after a write. 53bd529cfbSEvgeniy Polyakov */ 54bd529cfbSEvgeniy Polyakov static inline size_t w1_f23_fix_count(loff_t off, size_t count, size_t size) 55bd529cfbSEvgeniy Polyakov { 56bd529cfbSEvgeniy Polyakov if (off > size) 57bd529cfbSEvgeniy Polyakov return 0; 58bd529cfbSEvgeniy Polyakov 59bd529cfbSEvgeniy Polyakov if ((off + count) > size) 60bd529cfbSEvgeniy Polyakov return (size - off); 61bd529cfbSEvgeniy Polyakov 62bd529cfbSEvgeniy Polyakov return count; 63bd529cfbSEvgeniy Polyakov } 64bd529cfbSEvgeniy Polyakov 65e9d55f9dSEvgeniy Polyakov #ifdef CONFIG_W1_SLAVE_DS2433_CRC 66bd529cfbSEvgeniy Polyakov static int w1_f23_refresh_block(struct w1_slave *sl, struct w1_f23_data *data, 67bd529cfbSEvgeniy Polyakov int block) 68bd529cfbSEvgeniy Polyakov { 69bd529cfbSEvgeniy Polyakov u8 wrbuf[3]; 70bd529cfbSEvgeniy Polyakov int off = block * W1_PAGE_SIZE; 71bd529cfbSEvgeniy Polyakov 72bd529cfbSEvgeniy Polyakov if (data->validcrc & (1 << block)) 73bd529cfbSEvgeniy Polyakov return 0; 74bd529cfbSEvgeniy Polyakov 75bd529cfbSEvgeniy Polyakov if (w1_reset_select_slave(sl)) { 76bd529cfbSEvgeniy Polyakov data->validcrc = 0; 77bd529cfbSEvgeniy Polyakov return -EIO; 78bd529cfbSEvgeniy Polyakov } 79bd529cfbSEvgeniy Polyakov 80bd529cfbSEvgeniy Polyakov wrbuf[0] = W1_F23_READ_EEPROM; 81bd529cfbSEvgeniy Polyakov wrbuf[1] = off & 0xff; 82bd529cfbSEvgeniy Polyakov wrbuf[2] = off >> 8; 83bd529cfbSEvgeniy Polyakov w1_write_block(sl->master, wrbuf, 3); 84bd529cfbSEvgeniy Polyakov w1_read_block(sl->master, &data->memory[off], W1_PAGE_SIZE); 85bd529cfbSEvgeniy Polyakov 86bd529cfbSEvgeniy Polyakov /* cache the block if the CRC is valid */ 87bd529cfbSEvgeniy Polyakov if (crc16(CRC16_INIT, &data->memory[off], W1_PAGE_SIZE) == CRC16_VALID) 88bd529cfbSEvgeniy Polyakov data->validcrc |= (1 << block); 89bd529cfbSEvgeniy Polyakov 90bd529cfbSEvgeniy Polyakov return 0; 91bd529cfbSEvgeniy Polyakov } 92e9d55f9dSEvgeniy Polyakov #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ 93bd529cfbSEvgeniy Polyakov 94bd529cfbSEvgeniy Polyakov static ssize_t w1_f23_read_bin(struct kobject *kobj, char *buf, loff_t off, 95bd529cfbSEvgeniy Polyakov size_t count) 96bd529cfbSEvgeniy Polyakov { 97bd529cfbSEvgeniy Polyakov struct w1_slave *sl = kobj_to_w1_slave(kobj); 98e9d55f9dSEvgeniy Polyakov #ifdef CONFIG_W1_SLAVE_DS2433_CRC 99bd529cfbSEvgeniy Polyakov struct w1_f23_data *data = sl->family_data; 100bd529cfbSEvgeniy Polyakov int i, min_page, max_page; 101bd529cfbSEvgeniy Polyakov #else 102bd529cfbSEvgeniy Polyakov u8 wrbuf[3]; 103bd529cfbSEvgeniy Polyakov #endif 104bd529cfbSEvgeniy Polyakov 105bd529cfbSEvgeniy Polyakov if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0) 106bd529cfbSEvgeniy Polyakov return 0; 107bd529cfbSEvgeniy Polyakov 108abd52a13SEvgeniy Polyakov mutex_lock(&sl->master->mutex); 109bd529cfbSEvgeniy Polyakov 110e9d55f9dSEvgeniy Polyakov #ifdef CONFIG_W1_SLAVE_DS2433_CRC 111bd529cfbSEvgeniy Polyakov 112bd529cfbSEvgeniy Polyakov min_page = (off >> W1_PAGE_BITS); 113bd529cfbSEvgeniy Polyakov max_page = (off + count - 1) >> W1_PAGE_BITS; 114bd529cfbSEvgeniy Polyakov for (i = min_page; i <= max_page; i++) { 115bd529cfbSEvgeniy Polyakov if (w1_f23_refresh_block(sl, data, i)) { 116bd529cfbSEvgeniy Polyakov count = -EIO; 117bd529cfbSEvgeniy Polyakov goto out_up; 118bd529cfbSEvgeniy Polyakov } 119bd529cfbSEvgeniy Polyakov } 120bd529cfbSEvgeniy Polyakov memcpy(buf, &data->memory[off], count); 121bd529cfbSEvgeniy Polyakov 122e9d55f9dSEvgeniy Polyakov #else /* CONFIG_W1_SLAVE_DS2433_CRC */ 123bd529cfbSEvgeniy Polyakov 124bd529cfbSEvgeniy Polyakov /* read directly from the EEPROM */ 125bd529cfbSEvgeniy Polyakov if (w1_reset_select_slave(sl)) { 126bd529cfbSEvgeniy Polyakov count = -EIO; 127bd529cfbSEvgeniy Polyakov goto out_up; 128bd529cfbSEvgeniy Polyakov } 129bd529cfbSEvgeniy Polyakov 130bd529cfbSEvgeniy Polyakov wrbuf[0] = W1_F23_READ_EEPROM; 131bd529cfbSEvgeniy Polyakov wrbuf[1] = off & 0xff; 132bd529cfbSEvgeniy Polyakov wrbuf[2] = off >> 8; 133bd529cfbSEvgeniy Polyakov w1_write_block(sl->master, wrbuf, 3); 134bd529cfbSEvgeniy Polyakov w1_read_block(sl->master, buf, count); 135bd529cfbSEvgeniy Polyakov 136e9d55f9dSEvgeniy Polyakov #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ 137bd529cfbSEvgeniy Polyakov 138bd529cfbSEvgeniy Polyakov out_up: 139abd52a13SEvgeniy Polyakov mutex_unlock(&sl->master->mutex); 140bd529cfbSEvgeniy Polyakov 141bd529cfbSEvgeniy Polyakov return count; 142bd529cfbSEvgeniy Polyakov } 143bd529cfbSEvgeniy Polyakov 144bd529cfbSEvgeniy Polyakov /** 145bd529cfbSEvgeniy Polyakov * Writes to the scratchpad and reads it back for verification. 146bd529cfbSEvgeniy Polyakov * Then copies the scratchpad to EEPROM. 147bd529cfbSEvgeniy Polyakov * The data must be on one page. 148bd529cfbSEvgeniy Polyakov * The master must be locked. 149bd529cfbSEvgeniy Polyakov * 150bd529cfbSEvgeniy Polyakov * @param sl The slave structure 151bd529cfbSEvgeniy Polyakov * @param addr Address for the write 152bd529cfbSEvgeniy Polyakov * @param len length must be <= (W1_PAGE_SIZE - (addr & W1_PAGE_MASK)) 153bd529cfbSEvgeniy Polyakov * @param data The data to write 154bd529cfbSEvgeniy Polyakov * @return 0=Success -1=failure 155bd529cfbSEvgeniy Polyakov */ 156bd529cfbSEvgeniy Polyakov static int w1_f23_write(struct w1_slave *sl, int addr, int len, const u8 *data) 157bd529cfbSEvgeniy Polyakov { 158bd529cfbSEvgeniy Polyakov u8 wrbuf[4]; 159bd529cfbSEvgeniy Polyakov u8 rdbuf[W1_PAGE_SIZE + 3]; 160bd529cfbSEvgeniy Polyakov u8 es = (addr + len - 1) & 0x1f; 161bd529cfbSEvgeniy Polyakov 162bd529cfbSEvgeniy Polyakov /* Write the data to the scratchpad */ 163bd529cfbSEvgeniy Polyakov if (w1_reset_select_slave(sl)) 164bd529cfbSEvgeniy Polyakov return -1; 165bd529cfbSEvgeniy Polyakov 166bd529cfbSEvgeniy Polyakov wrbuf[0] = W1_F23_WRITE_SCRATCH; 167bd529cfbSEvgeniy Polyakov wrbuf[1] = addr & 0xff; 168bd529cfbSEvgeniy Polyakov wrbuf[2] = addr >> 8; 169bd529cfbSEvgeniy Polyakov 170bd529cfbSEvgeniy Polyakov w1_write_block(sl->master, wrbuf, 3); 171bd529cfbSEvgeniy Polyakov w1_write_block(sl->master, data, len); 172bd529cfbSEvgeniy Polyakov 173bd529cfbSEvgeniy Polyakov /* Read the scratchpad and verify */ 174bd529cfbSEvgeniy Polyakov if (w1_reset_select_slave(sl)) 175bd529cfbSEvgeniy Polyakov return -1; 176bd529cfbSEvgeniy Polyakov 177bd529cfbSEvgeniy Polyakov w1_write_8(sl->master, W1_F23_READ_SCRATCH); 178bd529cfbSEvgeniy Polyakov w1_read_block(sl->master, rdbuf, len + 3); 179bd529cfbSEvgeniy Polyakov 180bd529cfbSEvgeniy Polyakov /* Compare what was read against the data written */ 181bd529cfbSEvgeniy Polyakov if ((rdbuf[0] != wrbuf[1]) || (rdbuf[1] != wrbuf[2]) || 182bd529cfbSEvgeniy Polyakov (rdbuf[2] != es) || (memcmp(data, &rdbuf[3], len) != 0)) 183bd529cfbSEvgeniy Polyakov return -1; 184bd529cfbSEvgeniy Polyakov 185bd529cfbSEvgeniy Polyakov /* Copy the scratchpad to EEPROM */ 186bd529cfbSEvgeniy Polyakov if (w1_reset_select_slave(sl)) 187bd529cfbSEvgeniy Polyakov return -1; 188bd529cfbSEvgeniy Polyakov 189bd529cfbSEvgeniy Polyakov wrbuf[0] = W1_F23_COPY_SCRATCH; 190bd529cfbSEvgeniy Polyakov wrbuf[3] = es; 191bd529cfbSEvgeniy Polyakov w1_write_block(sl->master, wrbuf, 4); 192bd529cfbSEvgeniy Polyakov 193bd529cfbSEvgeniy Polyakov /* Sleep for 5 ms to wait for the write to complete */ 194bd529cfbSEvgeniy Polyakov msleep(5); 195bd529cfbSEvgeniy Polyakov 196bd529cfbSEvgeniy Polyakov /* Reset the bus to wake up the EEPROM (this may not be needed) */ 197bd529cfbSEvgeniy Polyakov w1_reset_bus(sl->master); 198bd529cfbSEvgeniy Polyakov 199bd529cfbSEvgeniy Polyakov return 0; 200bd529cfbSEvgeniy Polyakov } 201bd529cfbSEvgeniy Polyakov 202bd529cfbSEvgeniy Polyakov static ssize_t w1_f23_write_bin(struct kobject *kobj, char *buf, loff_t off, 203bd529cfbSEvgeniy Polyakov size_t count) 204bd529cfbSEvgeniy Polyakov { 205bd529cfbSEvgeniy Polyakov struct w1_slave *sl = kobj_to_w1_slave(kobj); 206bd529cfbSEvgeniy Polyakov int addr, len, idx; 207bd529cfbSEvgeniy Polyakov 208bd529cfbSEvgeniy Polyakov if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0) 209bd529cfbSEvgeniy Polyakov return 0; 210bd529cfbSEvgeniy Polyakov 211e9d55f9dSEvgeniy Polyakov #ifdef CONFIG_W1_SLAVE_DS2433_CRC 212bd529cfbSEvgeniy Polyakov /* can only write full blocks in cached mode */ 213bd529cfbSEvgeniy Polyakov if ((off & W1_PAGE_MASK) || (count & W1_PAGE_MASK)) { 214bd529cfbSEvgeniy Polyakov dev_err(&sl->dev, "invalid offset/count off=%d cnt=%zd\n", 215bd529cfbSEvgeniy Polyakov (int)off, count); 216bd529cfbSEvgeniy Polyakov return -EINVAL; 217bd529cfbSEvgeniy Polyakov } 218bd529cfbSEvgeniy Polyakov 219bd529cfbSEvgeniy Polyakov /* make sure the block CRCs are valid */ 220bd529cfbSEvgeniy Polyakov for (idx = 0; idx < count; idx += W1_PAGE_SIZE) { 221bd529cfbSEvgeniy Polyakov if (crc16(CRC16_INIT, &buf[idx], W1_PAGE_SIZE) != CRC16_VALID) { 222bd529cfbSEvgeniy Polyakov dev_err(&sl->dev, "bad CRC at offset %d\n", (int)off); 223bd529cfbSEvgeniy Polyakov return -EINVAL; 224bd529cfbSEvgeniy Polyakov } 225bd529cfbSEvgeniy Polyakov } 226e9d55f9dSEvgeniy Polyakov #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ 227bd529cfbSEvgeniy Polyakov 228abd52a13SEvgeniy Polyakov mutex_lock(&sl->master->mutex); 229bd529cfbSEvgeniy Polyakov 230bd529cfbSEvgeniy Polyakov /* Can only write data to one page at a time */ 231bd529cfbSEvgeniy Polyakov idx = 0; 232bd529cfbSEvgeniy Polyakov while (idx < count) { 233bd529cfbSEvgeniy Polyakov addr = off + idx; 234bd529cfbSEvgeniy Polyakov len = W1_PAGE_SIZE - (addr & W1_PAGE_MASK); 235bd529cfbSEvgeniy Polyakov if (len > (count - idx)) 236bd529cfbSEvgeniy Polyakov len = count - idx; 237bd529cfbSEvgeniy Polyakov 238bd529cfbSEvgeniy Polyakov if (w1_f23_write(sl, addr, len, &buf[idx]) < 0) { 239bd529cfbSEvgeniy Polyakov count = -EIO; 240bd529cfbSEvgeniy Polyakov goto out_up; 241bd529cfbSEvgeniy Polyakov } 242bd529cfbSEvgeniy Polyakov idx += len; 243bd529cfbSEvgeniy Polyakov } 244bd529cfbSEvgeniy Polyakov 245bd529cfbSEvgeniy Polyakov out_up: 246abd52a13SEvgeniy Polyakov mutex_unlock(&sl->master->mutex); 247bd529cfbSEvgeniy Polyakov 248bd529cfbSEvgeniy Polyakov return count; 249bd529cfbSEvgeniy Polyakov } 250bd529cfbSEvgeniy Polyakov 251bd529cfbSEvgeniy Polyakov static struct bin_attribute w1_f23_bin_attr = { 252bd529cfbSEvgeniy Polyakov .attr = { 253bd529cfbSEvgeniy Polyakov .name = "eeprom", 254bd529cfbSEvgeniy Polyakov .mode = S_IRUGO | S_IWUSR, 255bd529cfbSEvgeniy Polyakov .owner = THIS_MODULE, 256bd529cfbSEvgeniy Polyakov }, 257bd529cfbSEvgeniy Polyakov .size = W1_EEPROM_SIZE, 258bd529cfbSEvgeniy Polyakov .read = w1_f23_read_bin, 259bd529cfbSEvgeniy Polyakov .write = w1_f23_write_bin, 260bd529cfbSEvgeniy Polyakov }; 261bd529cfbSEvgeniy Polyakov 262bd529cfbSEvgeniy Polyakov static int w1_f23_add_slave(struct w1_slave *sl) 263bd529cfbSEvgeniy Polyakov { 264bd529cfbSEvgeniy Polyakov int err; 265e9d55f9dSEvgeniy Polyakov #ifdef CONFIG_W1_SLAVE_DS2433_CRC 266bd529cfbSEvgeniy Polyakov struct w1_f23_data *data; 267bd529cfbSEvgeniy Polyakov 268bd529cfbSEvgeniy Polyakov data = kmalloc(sizeof(struct w1_f23_data), GFP_KERNEL); 269bd529cfbSEvgeniy Polyakov if (!data) 270bd529cfbSEvgeniy Polyakov return -ENOMEM; 271bd529cfbSEvgeniy Polyakov memset(data, 0, sizeof(struct w1_f23_data)); 272bd529cfbSEvgeniy Polyakov sl->family_data = data; 273bd529cfbSEvgeniy Polyakov 274e9d55f9dSEvgeniy Polyakov #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ 275bd529cfbSEvgeniy Polyakov 276bd529cfbSEvgeniy Polyakov err = sysfs_create_bin_file(&sl->dev.kobj, &w1_f23_bin_attr); 277bd529cfbSEvgeniy Polyakov 278e9d55f9dSEvgeniy Polyakov #ifdef CONFIG_W1_SLAVE_DS2433_CRC 279bd529cfbSEvgeniy Polyakov if (err) 280bd529cfbSEvgeniy Polyakov kfree(data); 281e9d55f9dSEvgeniy Polyakov #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ 282bd529cfbSEvgeniy Polyakov 283bd529cfbSEvgeniy Polyakov return err; 284bd529cfbSEvgeniy Polyakov } 285bd529cfbSEvgeniy Polyakov 286bd529cfbSEvgeniy Polyakov static void w1_f23_remove_slave(struct w1_slave *sl) 287bd529cfbSEvgeniy Polyakov { 288e9d55f9dSEvgeniy Polyakov #ifdef CONFIG_W1_SLAVE_DS2433_CRC 289bd529cfbSEvgeniy Polyakov kfree(sl->family_data); 290bd529cfbSEvgeniy Polyakov sl->family_data = NULL; 291e9d55f9dSEvgeniy Polyakov #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ 292bd529cfbSEvgeniy Polyakov sysfs_remove_bin_file(&sl->dev.kobj, &w1_f23_bin_attr); 293bd529cfbSEvgeniy Polyakov } 294bd529cfbSEvgeniy Polyakov 295bd529cfbSEvgeniy Polyakov static struct w1_family_ops w1_f23_fops = { 296bd529cfbSEvgeniy Polyakov .add_slave = w1_f23_add_slave, 297bd529cfbSEvgeniy Polyakov .remove_slave = w1_f23_remove_slave, 298bd529cfbSEvgeniy Polyakov }; 299bd529cfbSEvgeniy Polyakov 300bd529cfbSEvgeniy Polyakov static struct w1_family w1_family_23 = { 301bd529cfbSEvgeniy Polyakov .fid = W1_EEPROM_DS2433, 302bd529cfbSEvgeniy Polyakov .fops = &w1_f23_fops, 303bd529cfbSEvgeniy Polyakov }; 304bd529cfbSEvgeniy Polyakov 305bd529cfbSEvgeniy Polyakov static int __init w1_f23_init(void) 306bd529cfbSEvgeniy Polyakov { 307bd529cfbSEvgeniy Polyakov return w1_register_family(&w1_family_23); 308bd529cfbSEvgeniy Polyakov } 309bd529cfbSEvgeniy Polyakov 310bd529cfbSEvgeniy Polyakov static void __exit w1_f23_fini(void) 311bd529cfbSEvgeniy Polyakov { 312bd529cfbSEvgeniy Polyakov w1_unregister_family(&w1_family_23); 313bd529cfbSEvgeniy Polyakov } 314bd529cfbSEvgeniy Polyakov 315bd529cfbSEvgeniy Polyakov module_init(w1_f23_init); 316bd529cfbSEvgeniy Polyakov module_exit(w1_f23_fini); 317