diff options
Diffstat (limited to 'drivers/w1/slaves')
| -rw-r--r-- | drivers/w1/slaves/Kconfig | 82 | ||||
| -rw-r--r-- | drivers/w1/slaves/Makefile | 9 | ||||
| -rw-r--r-- | drivers/w1/slaves/w1_bq27000.c | 117 | ||||
| -rw-r--r-- | drivers/w1/slaves/w1_ds2408.c | 366 | ||||
| -rw-r--r-- | drivers/w1/slaves/w1_ds2413.c | 150 | ||||
| -rw-r--r-- | drivers/w1/slaves/w1_ds2423.c | 158 | ||||
| -rw-r--r-- | drivers/w1/slaves/w1_ds2431.c | 308 | ||||
| -rw-r--r-- | drivers/w1/slaves/w1_ds2433.c | 64 | ||||
| -rw-r--r-- | drivers/w1/slaves/w1_ds2760.c | 113 | ||||
| -rw-r--r-- | drivers/w1/slaves/w1_ds2760.h | 7 | ||||
| -rw-r--r-- | drivers/w1/slaves/w1_ds2780.c | 191 | ||||
| -rw-r--r-- | drivers/w1/slaves/w1_ds2780.h | 129 | ||||
| -rw-r--r-- | drivers/w1/slaves/w1_ds2781.c | 189 | ||||
| -rw-r--r-- | drivers/w1/slaves/w1_ds2781.h | 134 | ||||
| -rw-r--r-- | drivers/w1/slaves/w1_ds28e04.c | 442 | ||||
| -rw-r--r-- | drivers/w1/slaves/w1_smem.c | 6 | ||||
| -rw-r--r-- | drivers/w1/slaves/w1_therm.c | 183 |
17 files changed, 2490 insertions, 158 deletions
diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index 3df29a122f8..1cdce80b6ab 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -16,6 +16,45 @@ config W1_SLAVE_SMEM Say Y here if you want to connect 1-wire simple 64bit memory rom(ds2401/ds2411/ds1990*) to your wire. +config W1_SLAVE_DS2408 + tristate "8-Channel Addressable Switch (IO Expander) 0x29 family support (DS2408)" + help + Say Y here if you want to use a 1-wire + DS2408 8-Channel Addressable Switch device support + +config W1_SLAVE_DS2408_READBACK + bool "Read-back values written to DS2408's output register" + depends on W1_SLAVE_DS2408 + default y + help + Enabling this will cause the driver to read back the values written + to the chip's output register in order to detect errors. + + This is slower but useful when debugging chips and/or busses. + +config W1_SLAVE_DS2413 + tristate "Dual Channel Addressable Switch 0x3a family support (DS2413)" + help + Say Y here if you want to use a 1-wire + DS2413 Dual Channel Addressable Switch device support + +config W1_SLAVE_DS2423 + tristate "Counter 1-wire device (DS2423)" + select CRC16 + help + If you enable this you can read the counter values available + in the DS2423 chipset from the w1_slave file under the + sys file system. + + Say Y here if you want to use a 1-wire + counter family device (DS2423). + +config W1_SLAVE_DS2431 + tristate "1kb EEPROM family support (DS2431)" + help + Say Y here if you want to use a 1-wire + 1kb EEPROM family device (DS2431) + config W1_SLAVE_DS2433 tristate "4kb EEPROM family support (DS2433)" help @@ -33,7 +72,6 @@ config W1_SLAVE_DS2433_CRC config W1_SLAVE_DS2760 tristate "Dallas 2760 battery monitor chip (HP iPAQ & others)" - depends on W1 help If you enable this you will have the DS2760 battery monitor chip support. @@ -44,4 +82,46 @@ config W1_SLAVE_DS2760 If you are unsure, say N. +config W1_SLAVE_DS2780 + tristate "Dallas 2780 battery monitor chip" + help + If you enable this you will have the DS2780 battery monitor + chip support. + + The battery monitor chip is used in many batteries/devices + as the one who is responsible for charging/discharging/monitoring + Li+ batteries. + + If you are unsure, say N. + +config W1_SLAVE_DS2781 + tristate "Dallas 2781 battery monitor chip" + help + If you enable this you will have the DS2781 battery monitor + chip support. + + The battery monitor chip is used in many batteries/devices + as the one who is responsible for charging/discharging/monitoring + Li+ batteries. + + If you are unsure, say N. + +config W1_SLAVE_DS28E04 + tristate "4096-Bit Addressable 1-Wire EEPROM with PIO (DS28E04-100)" + select CRC16 + help + If you enable this you will have the DS28E04-100 + chip support. + + Say Y here if you want to use a 1-wire + 4kb EEPROM with PIO family device (DS28E04). + + If you are unsure, say N. + +config W1_SLAVE_BQ27000 + tristate "BQ27000 slave support" + help + Say Y here if you want to use a hdq + bq27000 slave support. + endmenu diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index a8eb7524df1..06529f3157a 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile @@ -4,6 +4,13 @@ obj-$(CONFIG_W1_SLAVE_THERM) += w1_therm.o obj-$(CONFIG_W1_SLAVE_SMEM) += w1_smem.o +obj-$(CONFIG_W1_SLAVE_DS2408) += w1_ds2408.o +obj-$(CONFIG_W1_SLAVE_DS2413) += w1_ds2413.o +obj-$(CONFIG_W1_SLAVE_DS2423) += w1_ds2423.o +obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o - +obj-$(CONFIG_W1_SLAVE_DS2780) += w1_ds2780.o +obj-$(CONFIG_W1_SLAVE_DS2781) += w1_ds2781.o +obj-$(CONFIG_W1_SLAVE_BQ27000) += w1_bq27000.o +obj-$(CONFIG_W1_SLAVE_DS28E04) += w1_ds28e04.o diff --git a/drivers/w1/slaves/w1_bq27000.c b/drivers/w1/slaves/w1_bq27000.c new file mode 100644 index 00000000000..afbefed5f2c --- /dev/null +++ b/drivers/w1/slaves/w1_bq27000.c @@ -0,0 +1,117 @@ +/* + * drivers/w1/slaves/w1_bq27000.c + * + * Copyright (C) 2007 Texas Instruments, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/power/bq27x00_battery.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" + +#define HDQ_CMD_READ (0) +#define HDQ_CMD_WRITE (1<<7) + +static int F_ID; + +static int w1_bq27000_read(struct device *dev, unsigned int reg) +{ + u8 val; + struct w1_slave *sl = container_of(dev->parent, struct w1_slave, dev); + + mutex_lock(&sl->master->bus_mutex); + w1_write_8(sl->master, HDQ_CMD_READ | reg); + val = w1_read_8(sl->master); + mutex_unlock(&sl->master->bus_mutex); + + return val; +} + +static struct bq27000_platform_data bq27000_battery_info = { + .read = w1_bq27000_read, + .name = "bq27000-battery", +}; + +static int w1_bq27000_add_slave(struct w1_slave *sl) +{ + int ret; + struct platform_device *pdev; + + pdev = platform_device_alloc("bq27000-battery", -1); + if (!pdev) { + ret = -ENOMEM; + return ret; + } + ret = platform_device_add_data(pdev, + &bq27000_battery_info, + sizeof(bq27000_battery_info)); + if (ret) + goto pdev_add_failed; + pdev->dev.parent = &sl->dev; + + ret = platform_device_add(pdev); + if (ret) + goto pdev_add_failed; + + dev_set_drvdata(&sl->dev, pdev); + + goto success; + +pdev_add_failed: + platform_device_put(pdev); +success: + return ret; +} + +static void w1_bq27000_remove_slave(struct w1_slave *sl) +{ + struct platform_device *pdev = dev_get_drvdata(&sl->dev); + + platform_device_unregister(pdev); +} + +static struct w1_family_ops w1_bq27000_fops = { + .add_slave = w1_bq27000_add_slave, + .remove_slave = w1_bq27000_remove_slave, +}; + +static struct w1_family w1_bq27000_family = { + .fid = 1, + .fops = &w1_bq27000_fops, +}; + +static int __init w1_bq27000_init(void) +{ + if (F_ID) + w1_bq27000_family.fid = F_ID; + + return w1_register_family(&w1_bq27000_family); +} + +static void __exit w1_bq27000_exit(void) +{ + w1_unregister_family(&w1_bq27000_family); +} + + +module_init(w1_bq27000_init); +module_exit(w1_bq27000_exit); + +module_param(F_ID, int, S_IRUSR); +MODULE_PARM_DESC(F_ID, "1-wire slave FID for BQ device"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments Ltd"); +MODULE_DESCRIPTION("HDQ/1-wire slave driver bq27000 battery monitor chip"); diff --git a/drivers/w1/slaves/w1_ds2408.c b/drivers/w1/slaves/w1_ds2408.c new file mode 100644 index 00000000000..7dfa0e11688 --- /dev/null +++ b/drivers/w1/slaves/w1_ds2408.c @@ -0,0 +1,366 @@ +/* + * w1_ds2408.c - w1 family 29 (DS2408) driver + * + * Copyright (c) 2010 Jean-Francois Dagenais <dagenaisj@sonatest.com> + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/slab.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jean-Francois Dagenais <dagenaisj@sonatest.com>"); +MODULE_DESCRIPTION("w1 family 29 driver for DS2408 8 Pin IO"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2408)); + + +#define W1_F29_RETRIES 3 + +#define W1_F29_REG_LOGIG_STATE 0x88 /* R */ +#define W1_F29_REG_OUTPUT_LATCH_STATE 0x89 /* R */ +#define W1_F29_REG_ACTIVITY_LATCH_STATE 0x8A /* R */ +#define W1_F29_REG_COND_SEARCH_SELECT_MASK 0x8B /* RW */ +#define W1_F29_REG_COND_SEARCH_POL_SELECT 0x8C /* RW */ +#define W1_F29_REG_CONTROL_AND_STATUS 0x8D /* RW */ + +#define W1_F29_FUNC_READ_PIO_REGS 0xF0 +#define W1_F29_FUNC_CHANN_ACCESS_READ 0xF5 +#define W1_F29_FUNC_CHANN_ACCESS_WRITE 0x5A +/* also used to write the control/status reg (0x8D): */ +#define W1_F29_FUNC_WRITE_COND_SEARCH_REG 0xCC +#define W1_F29_FUNC_RESET_ACTIVITY_LATCHES 0xC3 + +#define W1_F29_SUCCESS_CONFIRM_BYTE 0xAA + +static int _read_reg(struct w1_slave *sl, u8 address, unsigned char* buf) +{ + u8 wrbuf[3]; + dev_dbg(&sl->dev, + "Reading with slave: %p, reg addr: %0#4x, buff addr: %p", + sl, (unsigned int)address, buf); + + if (!buf) + return -EINVAL; + + mutex_lock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex locked"); + + if (w1_reset_select_slave(sl)) { + mutex_unlock(&sl->master->bus_mutex); + return -EIO; + } + + wrbuf[0] = W1_F29_FUNC_READ_PIO_REGS; + wrbuf[1] = address; + wrbuf[2] = 0; + w1_write_block(sl->master, wrbuf, 3); + *buf = w1_read_8(sl->master); + + mutex_unlock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex unlocked"); + return 1; +} + +static ssize_t state_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, + size_t count) +{ + dev_dbg(&kobj_to_w1_slave(kobj)->dev, + "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", + bin_attr->attr.name, kobj, (unsigned int)off, count, buf); + if (count != 1 || off != 0) + return -EFAULT; + return _read_reg(kobj_to_w1_slave(kobj), W1_F29_REG_LOGIG_STATE, buf); +} + +static ssize_t output_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + dev_dbg(&kobj_to_w1_slave(kobj)->dev, + "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", + bin_attr->attr.name, kobj, (unsigned int)off, count, buf); + if (count != 1 || off != 0) + return -EFAULT; + return _read_reg(kobj_to_w1_slave(kobj), + W1_F29_REG_OUTPUT_LATCH_STATE, buf); +} + +static ssize_t activity_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + dev_dbg(&kobj_to_w1_slave(kobj)->dev, + "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", + bin_attr->attr.name, kobj, (unsigned int)off, count, buf); + if (count != 1 || off != 0) + return -EFAULT; + return _read_reg(kobj_to_w1_slave(kobj), + W1_F29_REG_ACTIVITY_LATCH_STATE, buf); +} + +static ssize_t cond_search_mask_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + dev_dbg(&kobj_to_w1_slave(kobj)->dev, + "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", + bin_attr->attr.name, kobj, (unsigned int)off, count, buf); + if (count != 1 || off != 0) + return -EFAULT; + return _read_reg(kobj_to_w1_slave(kobj), + W1_F29_REG_COND_SEARCH_SELECT_MASK, buf); +} + +static ssize_t cond_search_polarity_read(struct file *filp, + struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + if (count != 1 || off != 0) + return -EFAULT; + return _read_reg(kobj_to_w1_slave(kobj), + W1_F29_REG_COND_SEARCH_POL_SELECT, buf); +} + +static ssize_t status_control_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + if (count != 1 || off != 0) + return -EFAULT; + return _read_reg(kobj_to_w1_slave(kobj), + W1_F29_REG_CONTROL_AND_STATUS, buf); +} + +static ssize_t output_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + u8 w1_buf[3]; + u8 readBack; + unsigned int retries = W1_F29_RETRIES; + + if (count != 1 || off != 0) + return -EFAULT; + + dev_dbg(&sl->dev, "locking mutex for write_output"); + mutex_lock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex locked"); + + if (w1_reset_select_slave(sl)) + goto error; + + while (retries--) { + w1_buf[0] = W1_F29_FUNC_CHANN_ACCESS_WRITE; + w1_buf[1] = *buf; + w1_buf[2] = ~(*buf); + w1_write_block(sl->master, w1_buf, 3); + + readBack = w1_read_8(sl->master); + + if (readBack != W1_F29_SUCCESS_CONFIRM_BYTE) { + if (w1_reset_resume_command(sl->master)) + goto error; + /* try again, the slave is ready for a command */ + continue; + } + +#ifdef CONFIG_W1_SLAVE_DS2408_READBACK + /* here the master could read another byte which + would be the PIO reg (the actual pin logic state) + since in this driver we don't know which pins are + in and outs, there's no value to read the state and + compare. with (*buf) so end this command abruptly: */ + if (w1_reset_resume_command(sl->master)) + goto error; + + /* go read back the output latches */ + /* (the direct effect of the write above) */ + w1_buf[0] = W1_F29_FUNC_READ_PIO_REGS; + w1_buf[1] = W1_F29_REG_OUTPUT_LATCH_STATE; + w1_buf[2] = 0; + w1_write_block(sl->master, w1_buf, 3); + /* read the result of the READ_PIO_REGS command */ + if (w1_read_8(sl->master) == *buf) +#endif + { + /* success! */ + mutex_unlock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, + "mutex unlocked, retries:%d", retries); + return 1; + } + } +error: + mutex_unlock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex unlocked in error, retries:%d", retries); + + return -EIO; +} + + +/** + * Writing to the activity file resets the activity latches. + */ +static ssize_t activity_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + unsigned int retries = W1_F29_RETRIES; + + if (count != 1 || off != 0) + return -EFAULT; + + mutex_lock(&sl->master->bus_mutex); + + if (w1_reset_select_slave(sl)) + goto error; + + while (retries--) { + w1_write_8(sl->master, W1_F29_FUNC_RESET_ACTIVITY_LATCHES); + if (w1_read_8(sl->master) == W1_F29_SUCCESS_CONFIRM_BYTE) { + mutex_unlock(&sl->master->bus_mutex); + return 1; + } + if (w1_reset_resume_command(sl->master)) + goto error; + } + +error: + mutex_unlock(&sl->master->bus_mutex); + return -EIO; +} + +static ssize_t status_control_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + u8 w1_buf[4]; + unsigned int retries = W1_F29_RETRIES; + + if (count != 1 || off != 0) + return -EFAULT; + + mutex_lock(&sl->master->bus_mutex); + + if (w1_reset_select_slave(sl)) + goto error; + + while (retries--) { + w1_buf[0] = W1_F29_FUNC_WRITE_COND_SEARCH_REG; + w1_buf[1] = W1_F29_REG_CONTROL_AND_STATUS; + w1_buf[2] = 0; + w1_buf[3] = *buf; + + w1_write_block(sl->master, w1_buf, 4); + if (w1_reset_resume_command(sl->master)) + goto error; + + w1_buf[0] = W1_F29_FUNC_READ_PIO_REGS; + w1_buf[1] = W1_F29_REG_CONTROL_AND_STATUS; + w1_buf[2] = 0; + + w1_write_block(sl->master, w1_buf, 3); + if (w1_read_8(sl->master) == *buf) { + /* success! */ + mutex_unlock(&sl->master->bus_mutex); + return 1; + } + } +error: + mutex_unlock(&sl->master->bus_mutex); + + return -EIO; +} + +/* + * This is a special sequence we must do to ensure the P0 output is not stuck + * in test mode. This is described in rev 2 of the ds2408's datasheet + * (http://datasheets.maximintegrated.com/en/ds/DS2408.pdf) under + * "APPLICATION INFORMATION/Power-up timing". + */ +static int w1_f29_disable_test_mode(struct w1_slave *sl) +{ + int res; + u8 magic[10] = {0x96, }; + u64 rn = le64_to_cpu(*((u64*)&sl->reg_num)); + + memcpy(&magic[1], &rn, 8); + magic[9] = 0x3C; + + mutex_lock(&sl->master->bus_mutex); + + res = w1_reset_bus(sl->master); + if (res) + goto out; + w1_write_block(sl->master, magic, ARRAY_SIZE(magic)); + + res = w1_reset_bus(sl->master); +out: + mutex_unlock(&sl->master->bus_mutex); + return res; +} + +static BIN_ATTR_RO(state, 1); +static BIN_ATTR_RW(output, 1); +static BIN_ATTR_RW(activity, 1); +static BIN_ATTR_RO(cond_search_mask, 1); +static BIN_ATTR_RO(cond_search_polarity, 1); +static BIN_ATTR_RW(status_control, 1); + +static struct bin_attribute *w1_f29_bin_attrs[] = { + &bin_attr_state, + &bin_attr_output, + &bin_attr_activity, + &bin_attr_cond_search_mask, + &bin_attr_cond_search_polarity, + &bin_attr_status_control, + NULL, +}; + +static const struct attribute_group w1_f29_group = { + .bin_attrs = w1_f29_bin_attrs, +}; + +static const struct attribute_group *w1_f29_groups[] = { + &w1_f29_group, + NULL, +}; + +static struct w1_family_ops w1_f29_fops = { + .add_slave = w1_f29_disable_test_mode, + .groups = w1_f29_groups, +}; + +static struct w1_family w1_family_29 = { + .fid = W1_FAMILY_DS2408, + .fops = &w1_f29_fops, +}; + +static int __init w1_f29_init(void) +{ + return w1_register_family(&w1_family_29); +} + +static void __exit w1_f29_exit(void) +{ + w1_unregister_family(&w1_family_29); +} + +module_init(w1_f29_init); +module_exit(w1_f29_exit); diff --git a/drivers/w1/slaves/w1_ds2413.c b/drivers/w1/slaves/w1_ds2413.c new file mode 100644 index 00000000000..ee28fc1ff39 --- /dev/null +++ b/drivers/w1/slaves/w1_ds2413.c @@ -0,0 +1,150 @@ +/* + * w1_ds2413.c - w1 family 3a (DS2413) driver + * based on w1_ds2408.c by Jean-Francois Dagenais <dagenaisj@sonatest.com> + * + * Copyright (c) 2013 Mariusz Bialonczyk <manio@skyboo.net> + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/slab.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mariusz Bialonczyk <manio@skyboo.net>"); +MODULE_DESCRIPTION("w1 family 3a driver for DS2413 2 Pin IO"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2413)); + +#define W1_F3A_RETRIES 3 +#define W1_F3A_FUNC_PIO_ACCESS_READ 0xF5 +#define W1_F3A_FUNC_PIO_ACCESS_WRITE 0x5A +#define W1_F3A_SUCCESS_CONFIRM_BYTE 0xAA + +static ssize_t state_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, + size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + dev_dbg(&sl->dev, + "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", + bin_attr->attr.name, kobj, (unsigned int)off, count, buf); + + if (off != 0) + return 0; + if (!buf) + return -EINVAL; + + mutex_lock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex locked"); + + if (w1_reset_select_slave(sl)) { + mutex_unlock(&sl->master->bus_mutex); + return -EIO; + } + + w1_write_8(sl->master, W1_F3A_FUNC_PIO_ACCESS_READ); + *buf = w1_read_8(sl->master); + + mutex_unlock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex unlocked"); + + /* check for correct complement */ + if ((*buf & 0x0F) != ((~*buf >> 4) & 0x0F)) + return -EIO; + else + return 1; +} + +static BIN_ATTR_RO(state, 1); + +static ssize_t output_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + u8 w1_buf[3]; + unsigned int retries = W1_F3A_RETRIES; + + if (count != 1 || off != 0) + return -EFAULT; + + dev_dbg(&sl->dev, "locking mutex for write_output"); + mutex_lock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex locked"); + + if (w1_reset_select_slave(sl)) + goto error; + + /* according to the DS2413 datasheet the most significant 6 bits + should be set to "1"s, so do it now */ + *buf = *buf | 0xFC; + + while (retries--) { + w1_buf[0] = W1_F3A_FUNC_PIO_ACCESS_WRITE; + w1_buf[1] = *buf; + w1_buf[2] = ~(*buf); + w1_write_block(sl->master, w1_buf, 3); + + if (w1_read_8(sl->master) == W1_F3A_SUCCESS_CONFIRM_BYTE) { + mutex_unlock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex unlocked, retries:%d", retries); + return 1; + } + if (w1_reset_resume_command(sl->master)) + goto error; + } + +error: + mutex_unlock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex unlocked in error, retries:%d", retries); + return -EIO; +} + +static BIN_ATTR(output, S_IRUGO | S_IWUSR | S_IWGRP, NULL, output_write, 1); + +static struct bin_attribute *w1_f3a_bin_attrs[] = { + &bin_attr_state, + &bin_attr_output, + NULL, +}; + +static const struct attribute_group w1_f3a_group = { + .bin_attrs = w1_f3a_bin_attrs, +}; + +static const struct attribute_group *w1_f3a_groups[] = { + &w1_f3a_group, + NULL, +}; + +static struct w1_family_ops w1_f3a_fops = { + .groups = w1_f3a_groups, +}; + +static struct w1_family w1_family_3a = { + .fid = W1_FAMILY_DS2413, + .fops = &w1_f3a_fops, +}; + +static int __init w1_f3a_init(void) +{ + return w1_register_family(&w1_family_3a); +} + +static void __exit w1_f3a_exit(void) +{ + w1_unregister_family(&w1_family_3a); +} + +module_init(w1_f3a_init); +module_exit(w1_f3a_exit); diff --git a/drivers/w1/slaves/w1_ds2423.c b/drivers/w1/slaves/w1_ds2423.c new file mode 100644 index 00000000000..7e41b7d91fb --- /dev/null +++ b/drivers/w1/slaves/w1_ds2423.c @@ -0,0 +1,158 @@ +/* + * w1_ds2423.c + * + * Copyright (c) 2010 Mika Laitio <lamikr@pilppa.org> + * + * This driver will read and write the value of 4 counters to w1_slave file in + * sys filesystem. + * Inspired by the w1_therm and w1_ds2431 drivers. + * + * This program is free software; you can redistribute it and/or modify + * it under the therms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/crc16.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" + +#define CRC16_VALID 0xb001 +#define CRC16_INIT 0 + +#define COUNTER_COUNT 4 +#define READ_BYTE_COUNT 42 + +static ssize_t w1_slave_show(struct device *device, + struct device_attribute *attr, char *out_buf) +{ + struct w1_slave *sl = dev_to_w1_slave(device); + struct w1_master *dev = sl->master; + u8 rbuf[COUNTER_COUNT * READ_BYTE_COUNT]; + u8 wrbuf[3]; + int rom_addr; + int read_byte_count; + int result; + ssize_t c; + int ii; + int p; + int crc; + + c = PAGE_SIZE; + rom_addr = (12 << 5) + 31; + wrbuf[0] = 0xA5; + wrbuf[1] = rom_addr & 0xFF; + wrbuf[2] = rom_addr >> 8; + mutex_lock(&dev->bus_mutex); + if (!w1_reset_select_slave(sl)) { + w1_write_block(dev, wrbuf, 3); + read_byte_count = 0; + for (p = 0; p < 4; p++) { + /* + * 1 byte for first bytes in ram page read + * 4 bytes for counter + * 4 bytes for zero bits + * 2 bytes for crc + * 31 remaining bytes from the ram page + */ + read_byte_count += w1_read_block(dev, + rbuf + (p * READ_BYTE_COUNT), READ_BYTE_COUNT); + for (ii = 0; ii < READ_BYTE_COUNT; ++ii) + c -= snprintf(out_buf + PAGE_SIZE - c, + c, "%02x ", + rbuf[(p * READ_BYTE_COUNT) + ii]); + if (read_byte_count != (p + 1) * READ_BYTE_COUNT) { + dev_warn(device, + "w1_counter_read() returned %u bytes " + "instead of %d bytes wanted.\n", + read_byte_count, + READ_BYTE_COUNT); + c -= snprintf(out_buf + PAGE_SIZE - c, + c, "crc=NO\n"); + } else { + if (p == 0) { + crc = crc16(CRC16_INIT, wrbuf, 3); + crc = crc16(crc, rbuf, 11); + } else { + /* + * DS2423 calculates crc from all bytes + * read after the previous crc bytes. + */ + crc = crc16(CRC16_INIT, + (rbuf + 11) + + ((p - 1) * READ_BYTE_COUNT), + READ_BYTE_COUNT); + } + if (crc == CRC16_VALID) { + result = 0; + for (ii = 4; ii > 0; ii--) { + result <<= 8; + result |= rbuf[(p * + READ_BYTE_COUNT) + ii]; + } + c -= snprintf(out_buf + PAGE_SIZE - c, + c, "crc=YES c=%d\n", result); + } else { + c -= snprintf(out_buf + PAGE_SIZE - c, + c, "crc=NO\n"); + } + } + } + } else { + c -= snprintf(out_buf + PAGE_SIZE - c, c, "Connection error"); + } + mutex_unlock(&dev->bus_mutex); + return PAGE_SIZE - c; +} + +static DEVICE_ATTR_RO(w1_slave); + +static struct attribute *w1_f1d_attrs[] = { + &dev_attr_w1_slave.attr, + NULL, +}; +ATTRIBUTE_GROUPS(w1_f1d); + +static struct w1_family_ops w1_f1d_fops = { + .groups = w1_f1d_groups, +}; + +static struct w1_family w1_family_1d = { + .fid = W1_COUNTER_DS2423, + .fops = &w1_f1d_fops, +}; + +static int __init w1_f1d_init(void) +{ + return w1_register_family(&w1_family_1d); +} + +static void __exit w1_f1d_exit(void) +{ + w1_unregister_family(&w1_family_1d); +} + +module_init(w1_f1d_init); +module_exit(w1_f1d_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mika Laitio <lamikr@pilppa.org>"); +MODULE_DESCRIPTION("w1 family 1d driver for DS2423, 4 counters and 4kb ram"); +MODULE_ALIAS("w1-family-" __stringify(W1_COUNTER_DS2423)); diff --git a/drivers/w1/slaves/w1_ds2431.c b/drivers/w1/slaves/w1_ds2431.c new file mode 100644 index 00000000000..9c4ff9d28ad --- /dev/null +++ b/drivers/w1/slaves/w1_ds2431.c @@ -0,0 +1,308 @@ +/* + * w1_ds2431.c - w1 family 2d (DS2431) driver + * + * Copyright (c) 2008 Bernhard Weirich <bernhard.weirich@riedel.net> + * + * Heavily inspired by w1_DS2433 driver from Ben Gardner <bgardner@wabtec.com> + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/delay.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" + +#define W1_F2D_EEPROM_SIZE 128 +#define W1_F2D_PAGE_COUNT 4 +#define W1_F2D_PAGE_BITS 5 +#define W1_F2D_PAGE_SIZE (1<<W1_F2D_PAGE_BITS) +#define W1_F2D_PAGE_MASK 0x1F + +#define W1_F2D_SCRATCH_BITS 3 +#define W1_F2D_SCRATCH_SIZE (1<<W1_F2D_SCRATCH_BITS) +#define W1_F2D_SCRATCH_MASK (W1_F2D_SCRATCH_SIZE-1) + +#define W1_F2D_READ_EEPROM 0xF0 +#define W1_F2D_WRITE_SCRATCH 0x0F +#define W1_F2D_READ_SCRATCH 0xAA +#define W1_F2D_COPY_SCRATCH 0x55 + + +#define W1_F2D_TPROG_MS 11 + +#define W1_F2D_READ_RETRIES 10 +#define W1_F2D_READ_MAXLEN 8 + +/* + * Check the file size bounds and adjusts count as needed. + * This would not be needed if the file size didn't reset to 0 after a write. + */ +static inline size_t w1_f2d_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; +} + +/* + * Read a block from W1 ROM two times and compares the results. + * If they are equal they are returned, otherwise the read + * is repeated W1_F2D_READ_RETRIES times. + * + * count must not exceed W1_F2D_READ_MAXLEN. + */ +static int w1_f2d_readblock(struct w1_slave *sl, int off, int count, char *buf) +{ + u8 wrbuf[3]; + u8 cmp[W1_F2D_READ_MAXLEN]; + int tries = W1_F2D_READ_RETRIES; + + do { + wrbuf[0] = W1_F2D_READ_EEPROM; + wrbuf[1] = off & 0xff; + wrbuf[2] = off >> 8; + + if (w1_reset_select_slave(sl)) + return -1; + + w1_write_block(sl->master, wrbuf, 3); + w1_read_block(sl->master, buf, count); + + if (w1_reset_select_slave(sl)) + return -1; + + w1_write_block(sl->master, wrbuf, 3); + w1_read_block(sl->master, cmp, count); + + if (!memcmp(cmp, buf, count)) + return 0; + } while (--tries); + + dev_err(&sl->dev, "proof reading failed %d times\n", + W1_F2D_READ_RETRIES); + + return -1; +} + +static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + int todo = count; + + count = w1_f2d_fix_count(off, count, W1_F2D_EEPROM_SIZE); + if (count == 0) + return 0; + + mutex_lock(&sl->master->bus_mutex); + + /* read directly from the EEPROM in chunks of W1_F2D_READ_MAXLEN */ + while (todo > 0) { + int block_read; + + if (todo >= W1_F2D_READ_MAXLEN) + block_read = W1_F2D_READ_MAXLEN; + else + block_read = todo; + + if (w1_f2d_readblock(sl, off, block_read, buf) < 0) + count = -EIO; + + todo -= W1_F2D_READ_MAXLEN; + buf += W1_F2D_READ_MAXLEN; + off += W1_F2D_READ_MAXLEN; + } + + mutex_unlock(&sl->master->bus_mutex); + + return count; +} + +/* + * Writes to the scratchpad and reads it back for verification. + * Then copies the scratchpad to EEPROM. + * The data must be aligned at W1_F2D_SCRATCH_SIZE bytes and + * must be W1_F2D_SCRATCH_SIZE bytes long. + * The master must be locked. + * + * @param sl The slave structure + * @param addr Address for the write + * @param len length must be <= (W1_F2D_PAGE_SIZE - (addr & W1_F2D_PAGE_MASK)) + * @param data The data to write + * @return 0=Success -1=failure + */ +static int w1_f2d_write(struct w1_slave *sl, int addr, int len, const u8 *data) +{ + int tries = W1_F2D_READ_RETRIES; + u8 wrbuf[4]; + u8 rdbuf[W1_F2D_SCRATCH_SIZE + 3]; + u8 es = (addr + len - 1) % W1_F2D_SCRATCH_SIZE; + +retry: + + /* Write the data to the scratchpad */ + if (w1_reset_select_slave(sl)) + return -1; + + wrbuf[0] = W1_F2D_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_F2D_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)) { + + if (--tries) + goto retry; + + dev_err(&sl->dev, + "could not write to eeprom, scratchpad compare failed %d times\n", + W1_F2D_READ_RETRIES); + + return -1; + } + + /* Copy the scratchpad to EEPROM */ + if (w1_reset_select_slave(sl)) + return -1; + + wrbuf[0] = W1_F2D_COPY_SCRATCH; + wrbuf[3] = es; + w1_write_block(sl->master, wrbuf, 4); + + /* Sleep for tprog ms to wait for the write to complete */ + msleep(W1_F2D_TPROG_MS); + + /* Reset the bus to wake up the EEPROM */ + w1_reset_bus(sl->master); + + return 0; +} + +static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + int addr, len; + int copy; + + count = w1_f2d_fix_count(off, count, W1_F2D_EEPROM_SIZE); + if (count == 0) + return 0; + + mutex_lock(&sl->master->bus_mutex); + + /* Can only write data in blocks of the size of the scratchpad */ + addr = off; + len = count; + while (len > 0) { + + /* if len too short or addr not aligned */ + if (len < W1_F2D_SCRATCH_SIZE || addr & W1_F2D_SCRATCH_MASK) { + char tmp[W1_F2D_SCRATCH_SIZE]; + + /* read the block and update the parts to be written */ + if (w1_f2d_readblock(sl, addr & ~W1_F2D_SCRATCH_MASK, + W1_F2D_SCRATCH_SIZE, tmp)) { + count = -EIO; + goto out_up; + } + + /* copy at most to the boundary of the PAGE or len */ + copy = W1_F2D_SCRATCH_SIZE - + (addr & W1_F2D_SCRATCH_MASK); + + if (copy > len) + copy = len; + + memcpy(&tmp[addr & W1_F2D_SCRATCH_MASK], buf, copy); + if (w1_f2d_write(sl, addr & ~W1_F2D_SCRATCH_MASK, + W1_F2D_SCRATCH_SIZE, tmp) < 0) { + count = -EIO; + goto out_up; + } + } else { + + copy = W1_F2D_SCRATCH_SIZE; + if (w1_f2d_write(sl, addr, copy, buf) < 0) { + count = -EIO; + goto out_up; + } + } + buf += copy; + addr += copy; + len -= copy; + } + +out_up: + mutex_unlock(&sl->master->bus_mutex); + + return count; +} + +static BIN_ATTR_RW(eeprom, W1_F2D_EEPROM_SIZE); + +static struct bin_attribute *w1_f2d_bin_attrs[] = { + &bin_attr_eeprom, + NULL, +}; + +static const struct attribute_group w1_f2d_group = { + .bin_attrs = w1_f2d_bin_attrs, +}; + +static const struct attribute_group *w1_f2d_groups[] = { + &w1_f2d_group, + NULL, +}; + +static struct w1_family_ops w1_f2d_fops = { + .groups = w1_f2d_groups, +}; + +static struct w1_family w1_family_2d = { + .fid = W1_EEPROM_DS2431, + .fops = &w1_f2d_fops, +}; + +static int __init w1_f2d_init(void) +{ + return w1_register_family(&w1_family_2d); +} + +static void __exit w1_f2d_fini(void) +{ + w1_unregister_family(&w1_family_2d); +} + +module_init(w1_f2d_init); +module_exit(w1_f2d_fini); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Bernhard Weirich <bernhard.weirich@riedel.net>"); +MODULE_DESCRIPTION("w1 family 2d driver for DS2431, 1kb EEPROM"); +MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS2431)); diff --git a/drivers/w1/slaves/w1_ds2433.c b/drivers/w1/slaves/w1_ds2433.c index 858c16a544c..72319a968a9 100644 --- a/drivers/w1/slaves/w1_ds2433.c +++ b/drivers/w1/slaves/w1_ds2433.c @@ -13,6 +13,7 @@ #include <linux/device.h> #include <linux/types.h> #include <linux/delay.h> +#include <linux/slab.h> #ifdef CONFIG_W1_SLAVE_DS2433_CRC #include <linux/crc16.h> @@ -28,6 +29,7 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>"); MODULE_DESCRIPTION("w1 family 23 driver for DS2433, 4kb EEPROM"); +MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS2433)); #define W1_EEPROM_SIZE 512 #define W1_PAGE_COUNT 16 @@ -91,9 +93,9 @@ static int w1_f23_refresh_block(struct w1_slave *sl, struct w1_f23_data *data, } #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ -static ssize_t w1_f23_read_bin(struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); #ifdef CONFIG_W1_SLAVE_DS2433_CRC @@ -106,7 +108,7 @@ static ssize_t w1_f23_read_bin(struct kobject *kobj, if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0) return 0; - mutex_lock(&sl->master->mutex); + mutex_lock(&sl->master->bus_mutex); #ifdef CONFIG_W1_SLAVE_DS2433_CRC @@ -137,7 +139,7 @@ static ssize_t w1_f23_read_bin(struct kobject *kobj, #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ out_up: - mutex_unlock(&sl->master->mutex); + mutex_unlock(&sl->master->bus_mutex); return count; } @@ -156,6 +158,9 @@ out_up: */ static int w1_f23_write(struct w1_slave *sl, int addr, int len, const u8 *data) { +#ifdef CONFIG_W1_SLAVE_DS2433_CRC + struct w1_f23_data *f23 = sl->family_data; +#endif u8 wrbuf[4]; u8 rdbuf[W1_PAGE_SIZE + 3]; u8 es = (addr + len - 1) & 0x1f; @@ -196,13 +201,15 @@ static int w1_f23_write(struct w1_slave *sl, int addr, int len, const u8 *data) /* Reset the bus to wake up the EEPROM (this may not be needed) */ w1_reset_bus(sl->master); - +#ifdef CONFIG_W1_SLAVE_DS2433_CRC + f23->validcrc &= ~(1 << (addr >> W1_PAGE_BITS)); +#endif return 0; } -static ssize_t w1_f23_write_bin(struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); int addr, len, idx; @@ -227,7 +234,7 @@ static ssize_t w1_f23_write_bin(struct kobject *kobj, } #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ - mutex_lock(&sl->master->mutex); + mutex_lock(&sl->master->bus_mutex); /* Can only write data to one page at a time */ idx = 0; @@ -245,24 +252,29 @@ static ssize_t w1_f23_write_bin(struct kobject *kobj, } out_up: - mutex_unlock(&sl->master->mutex); + mutex_unlock(&sl->master->bus_mutex); 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 BIN_ATTR_RW(eeprom, W1_EEPROM_SIZE); + +static struct bin_attribute *w1_f23_bin_attributes[] = { + &bin_attr_eeprom, + NULL, +}; + +static const struct attribute_group w1_f23_group = { + .bin_attrs = w1_f23_bin_attributes, +}; + +static const struct attribute_group *w1_f23_groups[] = { + &w1_f23_group, + NULL, }; static int w1_f23_add_slave(struct w1_slave *sl) { - int err; #ifdef CONFIG_W1_SLAVE_DS2433_CRC struct w1_f23_data *data; @@ -272,15 +284,7 @@ static int w1_f23_add_slave(struct w1_slave *sl) sl->family_data = data; #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ - - err = sysfs_create_bin_file(&sl->dev.kobj, &w1_f23_bin_attr); - -#ifdef CONFIG_W1_SLAVE_DS2433_CRC - if (err) - kfree(data); -#endif /* CONFIG_W1_SLAVE_DS2433_CRC */ - - return err; + return 0; } static void w1_f23_remove_slave(struct w1_slave *sl) @@ -289,12 +293,12 @@ static void w1_f23_remove_slave(struct w1_slave *sl) kfree(sl->family_data); sl->family_data = NULL; #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ - 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, + .groups = w1_f23_groups, }; static struct w1_family w1_family_23 = { diff --git a/drivers/w1/slaves/w1_ds2760.c b/drivers/w1/slaves/w1_ds2760.c index ed6b0576208..65f90dccd60 100644 --- a/drivers/w1/slaves/w1_ds2760.c +++ b/drivers/w1/slaves/w1_ds2760.c @@ -16,6 +16,7 @@ #include <linux/platform_device.h> #include <linux/mutex.h> #include <linux/idr.h> +#include <linux/gfp.h> #include "../w1.h" #include "../w1_int.h" @@ -30,7 +31,7 @@ static int w1_ds2760_io(struct device *dev, char *buf, int addr, size_t count, if (!dev) return 0; - mutex_lock(&sl->master->mutex); + mutex_lock(&sl->master->bus_mutex); if (addr > DS2760_DATA_SIZE || addr < 0) { count = 0; @@ -53,7 +54,7 @@ static int w1_ds2760_io(struct device *dev, char *buf, int addr, size_t count, } out: - mutex_unlock(&sl->master->mutex); + mutex_unlock(&sl->master->bus_mutex); return count; } @@ -68,69 +69,67 @@ int w1_ds2760_write(struct device *dev, char *buf, int addr, size_t count) return w1_ds2760_io(dev, buf, addr, count, 1); } -static ssize_t w1_ds2760_read_bin(struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static int w1_ds2760_eeprom_cmd(struct device *dev, int addr, int cmd) { - struct device *dev = container_of(kobj, struct device, kobj); - return w1_ds2760_read(dev, buf, off, count); -} - -static struct bin_attribute w1_ds2760_bin_attr = { - .attr = { - .name = "w1_slave", - .mode = S_IRUGO, - .owner = THIS_MODULE, - }, - .size = DS2760_DATA_SIZE, - .read = w1_ds2760_read_bin, -}; - -static DEFINE_IDR(bat_idr); -static DEFINE_MUTEX(bat_idr_lock); + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); -static int new_bat_id(void) -{ - int ret; + if (!dev) + return -EINVAL; - while (1) { - int id; + mutex_lock(&sl->master->bus_mutex); - ret = idr_pre_get(&bat_idr, GFP_KERNEL); - if (ret == 0) - return -ENOMEM; + if (w1_reset_select_slave(sl) == 0) { + w1_write_8(sl->master, cmd); + w1_write_8(sl->master, addr); + } - mutex_lock(&bat_idr_lock); - ret = idr_get_new(&bat_idr, NULL, &id); - mutex_unlock(&bat_idr_lock); + mutex_unlock(&sl->master->bus_mutex); + return 0; +} - if (ret == 0) { - ret = id & MAX_ID_MASK; - break; - } else if (ret == -EAGAIN) { - continue; - } else { - break; - } - } +int w1_ds2760_store_eeprom(struct device *dev, int addr) +{ + return w1_ds2760_eeprom_cmd(dev, addr, W1_DS2760_COPY_DATA); +} - return ret; +int w1_ds2760_recall_eeprom(struct device *dev, int addr) +{ + return w1_ds2760_eeprom_cmd(dev, addr, W1_DS2760_RECALL_DATA); } -static void release_bat_id(int id) +static ssize_t w1_slave_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) { - mutex_lock(&bat_idr_lock); - idr_remove(&bat_idr, id); - mutex_unlock(&bat_idr_lock); + struct device *dev = container_of(kobj, struct device, kobj); + return w1_ds2760_read(dev, buf, off, count); } +static BIN_ATTR_RO(w1_slave, DS2760_DATA_SIZE); + +static struct bin_attribute *w1_ds2760_bin_attrs[] = { + &bin_attr_w1_slave, + NULL, +}; + +static const struct attribute_group w1_ds2760_group = { + .bin_attrs = w1_ds2760_bin_attrs, +}; + +static const struct attribute_group *w1_ds2760_groups[] = { + &w1_ds2760_group, + NULL, +}; + +static DEFINE_IDA(bat_ida); + static int w1_ds2760_add_slave(struct w1_slave *sl) { int ret; int id; struct platform_device *pdev; - id = new_bat_id(); + id = ida_simple_get(&bat_ida, 0, 0, GFP_KERNEL); if (id < 0) { ret = id; goto noid; @@ -147,19 +146,14 @@ static int w1_ds2760_add_slave(struct w1_slave *sl) if (ret) goto pdev_add_failed; - ret = sysfs_create_bin_file(&sl->dev.kobj, &w1_ds2760_bin_attr); - if (ret) - goto bin_attr_failed; - dev_set_drvdata(&sl->dev, pdev); goto success; -bin_attr_failed: pdev_add_failed: - platform_device_unregister(pdev); + platform_device_put(pdev); pdev_alloc_failed: - release_bat_id(id); + ida_simple_remove(&bat_ida, id); noid: success: return ret; @@ -171,13 +165,13 @@ static void w1_ds2760_remove_slave(struct w1_slave *sl) int id = pdev->id; platform_device_unregister(pdev); - release_bat_id(id); - sysfs_remove_bin_file(&sl->dev.kobj, &w1_ds2760_bin_attr); + ida_simple_remove(&bat_ida, id); } static struct w1_family_ops w1_ds2760_fops = { .add_slave = w1_ds2760_add_slave, .remove_slave = w1_ds2760_remove_slave, + .groups = w1_ds2760_groups, }; static struct w1_family w1_ds2760_family = { @@ -189,18 +183,20 @@ static int __init w1_ds2760_init(void) { printk(KERN_INFO "1-Wire driver for the DS2760 battery monitor " " chip - (c) 2004-2005, Szabolcs Gyurko\n"); - idr_init(&bat_idr); + ida_init(&bat_ida); return w1_register_family(&w1_ds2760_family); } static void __exit w1_ds2760_exit(void) { w1_unregister_family(&w1_ds2760_family); - idr_destroy(&bat_idr); + ida_destroy(&bat_ida); } EXPORT_SYMBOL(w1_ds2760_read); EXPORT_SYMBOL(w1_ds2760_write); +EXPORT_SYMBOL(w1_ds2760_store_eeprom); +EXPORT_SYMBOL(w1_ds2760_recall_eeprom); module_init(w1_ds2760_init); module_exit(w1_ds2760_exit); @@ -208,3 +204,4 @@ module_exit(w1_ds2760_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>"); MODULE_DESCRIPTION("1-wire Driver Dallas 2760 battery monitor chip"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2760)); diff --git a/drivers/w1/slaves/w1_ds2760.h b/drivers/w1/slaves/w1_ds2760.h index f1302429cb0..58e77414156 100644 --- a/drivers/w1/slaves/w1_ds2760.h +++ b/drivers/w1/slaves/w1_ds2760.h @@ -25,6 +25,10 @@ #define DS2760_PROTECTION_REG 0x00 #define DS2760_STATUS_REG 0x01 + #define DS2760_STATUS_IE (1 << 2) + #define DS2760_STATUS_SWEN (1 << 3) + #define DS2760_STATUS_RNAOP (1 << 4) + #define DS2760_STATUS_PMOD (1 << 5) #define DS2760_EEPROM_REG 0x07 #define DS2760_SPECIAL_FEATURE_REG 0x08 #define DS2760_VOLTAGE_MSB 0x0c @@ -38,6 +42,7 @@ #define DS2760_EEPROM_BLOCK0 0x20 #define DS2760_ACTIVE_FULL 0x20 #define DS2760_EEPROM_BLOCK1 0x30 +#define DS2760_STATUS_WRITE_REG 0x31 #define DS2760_RATED_CAPACITY 0x32 #define DS2760_CURRENT_OFFSET_BIAS 0x33 #define DS2760_ACTIVE_EMPTY 0x3b @@ -46,5 +51,7 @@ extern int w1_ds2760_read(struct device *dev, char *buf, int addr, size_t count); extern int w1_ds2760_write(struct device *dev, char *buf, int addr, size_t count); +extern int w1_ds2760_store_eeprom(struct device *dev, int addr); +extern int w1_ds2760_recall_eeprom(struct device *dev, int addr); #endif /* !__w1_ds2760_h__ */ diff --git a/drivers/w1/slaves/w1_ds2780.c b/drivers/w1/slaves/w1_ds2780.c new file mode 100644 index 00000000000..50e85f7929d --- /dev/null +++ b/drivers/w1/slaves/w1_ds2780.c @@ -0,0 +1,191 @@ +/* + * 1-Wire implementation for the ds2780 chip + * + * Copyright (C) 2010 Indesign, LLC + * + * Author: Clifton Barnes <cabarnes@indesign-llc.com> + * + * Based on w1-ds2760 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/idr.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" +#include "w1_ds2780.h" + +static int w1_ds2780_do_io(struct device *dev, char *buf, int addr, + size_t count, int io) +{ + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + + if (addr > DS2780_DATA_SIZE || addr < 0) + return 0; + + count = min_t(int, count, DS2780_DATA_SIZE - addr); + + if (w1_reset_select_slave(sl) == 0) { + if (io) { + w1_write_8(sl->master, W1_DS2780_WRITE_DATA); + w1_write_8(sl->master, addr); + w1_write_block(sl->master, buf, count); + } else { + w1_write_8(sl->master, W1_DS2780_READ_DATA); + w1_write_8(sl->master, addr); + count = w1_read_block(sl->master, buf, count); + } + } + + return count; +} + +int w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count, + int io) +{ + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + int ret; + + if (!dev) + return -ENODEV; + + mutex_lock(&sl->master->bus_mutex); + + ret = w1_ds2780_do_io(dev, buf, addr, count, io); + + mutex_unlock(&sl->master->bus_mutex); + + return ret; +} +EXPORT_SYMBOL(w1_ds2780_io); + +int w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd) +{ + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + + if (!dev) + return -EINVAL; + + mutex_lock(&sl->master->bus_mutex); + + if (w1_reset_select_slave(sl) == 0) { + w1_write_8(sl->master, cmd); + w1_write_8(sl->master, addr); + } + + mutex_unlock(&sl->master->bus_mutex); + return 0; +} +EXPORT_SYMBOL(w1_ds2780_eeprom_cmd); + +static ssize_t w1_slave_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + return w1_ds2780_io(dev, buf, off, count, 0); +} + +static BIN_ATTR_RO(w1_slave, DS2780_DATA_SIZE); + +static struct bin_attribute *w1_ds2780_bin_attrs[] = { + &bin_attr_w1_slave, + NULL, +}; + +static const struct attribute_group w1_ds2780_group = { + .bin_attrs = w1_ds2780_bin_attrs, +}; + +static const struct attribute_group *w1_ds2780_groups[] = { + &w1_ds2780_group, + NULL, +}; + +static DEFINE_IDA(bat_ida); + +static int w1_ds2780_add_slave(struct w1_slave *sl) +{ + int ret; + int id; + struct platform_device *pdev; + + id = ida_simple_get(&bat_ida, 0, 0, GFP_KERNEL); + if (id < 0) { + ret = id; + goto noid; + } + + pdev = platform_device_alloc("ds2780-battery", id); + if (!pdev) { + ret = -ENOMEM; + goto pdev_alloc_failed; + } + pdev->dev.parent = &sl->dev; + + ret = platform_device_add(pdev); + if (ret) + goto pdev_add_failed; + + dev_set_drvdata(&sl->dev, pdev); + + return 0; + +pdev_add_failed: + platform_device_put(pdev); +pdev_alloc_failed: + ida_simple_remove(&bat_ida, id); +noid: + return ret; +} + +static void w1_ds2780_remove_slave(struct w1_slave *sl) +{ + struct platform_device *pdev = dev_get_drvdata(&sl->dev); + int id = pdev->id; + + platform_device_unregister(pdev); + ida_simple_remove(&bat_ida, id); +} + +static struct w1_family_ops w1_ds2780_fops = { + .add_slave = w1_ds2780_add_slave, + .remove_slave = w1_ds2780_remove_slave, + .groups = w1_ds2780_groups, +}; + +static struct w1_family w1_ds2780_family = { + .fid = W1_FAMILY_DS2780, + .fops = &w1_ds2780_fops, +}; + +static int __init w1_ds2780_init(void) +{ + ida_init(&bat_ida); + return w1_register_family(&w1_ds2780_family); +} + +static void __exit w1_ds2780_exit(void) +{ + w1_unregister_family(&w1_ds2780_family); + ida_destroy(&bat_ida); +} + +module_init(w1_ds2780_init); +module_exit(w1_ds2780_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Clifton Barnes <cabarnes@indesign-llc.com>"); +MODULE_DESCRIPTION("1-wire Driver for Maxim/Dallas DS2780 Stand-Alone Fuel Gauge IC"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2780)); diff --git a/drivers/w1/slaves/w1_ds2780.h b/drivers/w1/slaves/w1_ds2780.h new file mode 100644 index 00000000000..a1fba79eb1b --- /dev/null +++ b/drivers/w1/slaves/w1_ds2780.h @@ -0,0 +1,129 @@ +/* + * 1-Wire implementation for the ds2780 chip + * + * Copyright (C) 2010 Indesign, LLC + * + * Author: Clifton Barnes <cabarnes@indesign-llc.com> + * + * Based on w1-ds2760 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef _W1_DS2780_H +#define _W1_DS2780_H + +/* Function commands */ +#define W1_DS2780_READ_DATA 0x69 +#define W1_DS2780_WRITE_DATA 0x6C +#define W1_DS2780_COPY_DATA 0x48 +#define W1_DS2780_RECALL_DATA 0xB8 +#define W1_DS2780_LOCK 0x6A + +/* Register map */ +/* Register 0x00 Reserved */ +#define DS2780_STATUS_REG 0x01 +#define DS2780_RAAC_MSB_REG 0x02 +#define DS2780_RAAC_LSB_REG 0x03 +#define DS2780_RSAC_MSB_REG 0x04 +#define DS2780_RSAC_LSB_REG 0x05 +#define DS2780_RARC_REG 0x06 +#define DS2780_RSRC_REG 0x07 +#define DS2780_IAVG_MSB_REG 0x08 +#define DS2780_IAVG_LSB_REG 0x09 +#define DS2780_TEMP_MSB_REG 0x0A +#define DS2780_TEMP_LSB_REG 0x0B +#define DS2780_VOLT_MSB_REG 0x0C +#define DS2780_VOLT_LSB_REG 0x0D +#define DS2780_CURRENT_MSB_REG 0x0E +#define DS2780_CURRENT_LSB_REG 0x0F +#define DS2780_ACR_MSB_REG 0x10 +#define DS2780_ACR_LSB_REG 0x11 +#define DS2780_ACRL_MSB_REG 0x12 +#define DS2780_ACRL_LSB_REG 0x13 +#define DS2780_AS_REG 0x14 +#define DS2780_SFR_REG 0x15 +#define DS2780_FULL_MSB_REG 0x16 +#define DS2780_FULL_LSB_REG 0x17 +#define DS2780_AE_MSB_REG 0x18 +#define DS2780_AE_LSB_REG 0x19 +#define DS2780_SE_MSB_REG 0x1A +#define DS2780_SE_LSB_REG 0x1B +/* Register 0x1C - 0x1E Reserved */ +#define DS2780_EEPROM_REG 0x1F +#define DS2780_EEPROM_BLOCK0_START 0x20 +/* Register 0x20 - 0x2F User EEPROM */ +#define DS2780_EEPROM_BLOCK0_END 0x2F +/* Register 0x30 - 0x5F Reserved */ +#define DS2780_EEPROM_BLOCK1_START 0x60 +#define DS2780_CONTROL_REG 0x60 +#define DS2780_AB_REG 0x61 +#define DS2780_AC_MSB_REG 0x62 +#define DS2780_AC_LSB_REG 0x63 +#define DS2780_VCHG_REG 0x64 +#define DS2780_IMIN_REG 0x65 +#define DS2780_VAE_REG 0x66 +#define DS2780_IAE_REG 0x67 +#define DS2780_AE_40_REG 0x68 +#define DS2780_RSNSP_REG 0x69 +#define DS2780_FULL_40_MSB_REG 0x6A +#define DS2780_FULL_40_LSB_REG 0x6B +#define DS2780_FULL_3040_SLOPE_REG 0x6C +#define DS2780_FULL_2030_SLOPE_REG 0x6D +#define DS2780_FULL_1020_SLOPE_REG 0x6E +#define DS2780_FULL_0010_SLOPE_REG 0x6F +#define DS2780_AE_3040_SLOPE_REG 0x70 +#define DS2780_AE_2030_SLOPE_REG 0x71 +#define DS2780_AE_1020_SLOPE_REG 0x72 +#define DS2780_AE_0010_SLOPE_REG 0x73 +#define DS2780_SE_3040_SLOPE_REG 0x74 +#define DS2780_SE_2030_SLOPE_REG 0x75 +#define DS2780_SE_1020_SLOPE_REG 0x76 +#define DS2780_SE_0010_SLOPE_REG 0x77 +#define DS2780_RSGAIN_MSB_REG 0x78 +#define DS2780_RSGAIN_LSB_REG 0x79 +#define DS2780_RSTC_REG 0x7A +#define DS2780_FRSGAIN_MSB_REG 0x7B +#define DS2780_FRSGAIN_LSB_REG 0x7C +#define DS2780_EEPROM_BLOCK1_END 0x7C +/* Register 0x7D - 0xFF Reserved */ + +/* Number of valid register addresses */ +#define DS2780_DATA_SIZE 0x80 + +/* Status register bits */ +#define DS2780_STATUS_REG_CHGTF (1 << 7) +#define DS2780_STATUS_REG_AEF (1 << 6) +#define DS2780_STATUS_REG_SEF (1 << 5) +#define DS2780_STATUS_REG_LEARNF (1 << 4) +/* Bit 3 Reserved */ +#define DS2780_STATUS_REG_UVF (1 << 2) +#define DS2780_STATUS_REG_PORF (1 << 1) +/* Bit 0 Reserved */ + +/* Control register bits */ +/* Bit 7 Reserved */ +#define DS2780_CONTROL_REG_UVEN (1 << 6) +#define DS2780_CONTROL_REG_PMOD (1 << 5) +#define DS2780_CONTROL_REG_RNAOP (1 << 4) +/* Bit 0 - 3 Reserved */ + +/* Special feature register bits */ +/* Bit 1 - 7 Reserved */ +#define DS2780_SFR_REG_PIOSC (1 << 0) + +/* EEPROM register bits */ +#define DS2780_EEPROM_REG_EEC (1 << 7) +#define DS2780_EEPROM_REG_LOCK (1 << 6) +/* Bit 2 - 6 Reserved */ +#define DS2780_EEPROM_REG_BL1 (1 << 1) +#define DS2780_EEPROM_REG_BL0 (1 << 0) + +extern int w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count, + int io); +extern int w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd); + +#endif /* !_W1_DS2780_H */ diff --git a/drivers/w1/slaves/w1_ds2781.c b/drivers/w1/slaves/w1_ds2781.c new file mode 100644 index 00000000000..1eb98fb1688 --- /dev/null +++ b/drivers/w1/slaves/w1_ds2781.c @@ -0,0 +1,189 @@ +/* + * 1-Wire implementation for the ds2781 chip + * + * Author: Renata Sayakhova <renata@oktetlabs.ru> + * + * Based on w1-ds2780 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/idr.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" +#include "w1_ds2781.h" + +static int w1_ds2781_do_io(struct device *dev, char *buf, int addr, + size_t count, int io) +{ + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + + if (addr > DS2781_DATA_SIZE || addr < 0) + return 0; + + count = min_t(int, count, DS2781_DATA_SIZE - addr); + + if (w1_reset_select_slave(sl) == 0) { + if (io) { + w1_write_8(sl->master, W1_DS2781_WRITE_DATA); + w1_write_8(sl->master, addr); + w1_write_block(sl->master, buf, count); + } else { + w1_write_8(sl->master, W1_DS2781_READ_DATA); + w1_write_8(sl->master, addr); + count = w1_read_block(sl->master, buf, count); + } + } + + return count; +} + +int w1_ds2781_io(struct device *dev, char *buf, int addr, size_t count, + int io) +{ + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + int ret; + + if (!dev) + return -ENODEV; + + mutex_lock(&sl->master->bus_mutex); + + ret = w1_ds2781_do_io(dev, buf, addr, count, io); + + mutex_unlock(&sl->master->bus_mutex); + + return ret; +} +EXPORT_SYMBOL(w1_ds2781_io); + +int w1_ds2781_eeprom_cmd(struct device *dev, int addr, int cmd) +{ + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + + if (!dev) + return -EINVAL; + + mutex_lock(&sl->master->bus_mutex); + + if (w1_reset_select_slave(sl) == 0) { + w1_write_8(sl->master, cmd); + w1_write_8(sl->master, addr); + } + + mutex_unlock(&sl->master->bus_mutex); + return 0; +} +EXPORT_SYMBOL(w1_ds2781_eeprom_cmd); + +static ssize_t w1_slave_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + return w1_ds2781_io(dev, buf, off, count, 0); +} + +static BIN_ATTR_RO(w1_slave, DS2781_DATA_SIZE); + +static struct bin_attribute *w1_ds2781_bin_attrs[] = { + &bin_attr_w1_slave, + NULL, +}; + +static const struct attribute_group w1_ds2781_group = { + .bin_attrs = w1_ds2781_bin_attrs, +}; + +static const struct attribute_group *w1_ds2781_groups[] = { + &w1_ds2781_group, + NULL, +}; + +static DEFINE_IDA(bat_ida); + +static int w1_ds2781_add_slave(struct w1_slave *sl) +{ + int ret; + int id; + struct platform_device *pdev; + + id = ida_simple_get(&bat_ida, 0, 0, GFP_KERNEL); + if (id < 0) { + ret = id; + goto noid; + } + + pdev = platform_device_alloc("ds2781-battery", id); + if (!pdev) { + ret = -ENOMEM; + goto pdev_alloc_failed; + } + pdev->dev.parent = &sl->dev; + + ret = platform_device_add(pdev); + if (ret) + goto pdev_add_failed; + + dev_set_drvdata(&sl->dev, pdev); + + return 0; + +pdev_add_failed: + platform_device_put(pdev); +pdev_alloc_failed: + ida_simple_remove(&bat_ida, id); +noid: + return ret; +} + +static void w1_ds2781_remove_slave(struct w1_slave *sl) +{ + struct platform_device *pdev = dev_get_drvdata(&sl->dev); + int id = pdev->id; + + platform_device_unregister(pdev); + ida_simple_remove(&bat_ida, id); +} + +static struct w1_family_ops w1_ds2781_fops = { + .add_slave = w1_ds2781_add_slave, + .remove_slave = w1_ds2781_remove_slave, + .groups = w1_ds2781_groups, +}; + +static struct w1_family w1_ds2781_family = { + .fid = W1_FAMILY_DS2781, + .fops = &w1_ds2781_fops, +}; + +static int __init w1_ds2781_init(void) +{ + ida_init(&bat_ida); + return w1_register_family(&w1_ds2781_family); +} + +static void __exit w1_ds2781_exit(void) +{ + w1_unregister_family(&w1_ds2781_family); + ida_destroy(&bat_ida); +} + +module_init(w1_ds2781_init); +module_exit(w1_ds2781_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Renata Sayakhova <renata@oktetlabs.ru>"); +MODULE_DESCRIPTION("1-wire Driver for Maxim/Dallas DS2781 Stand-Alone Fuel Gauge IC"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2781)); diff --git a/drivers/w1/slaves/w1_ds2781.h b/drivers/w1/slaves/w1_ds2781.h new file mode 100644 index 00000000000..557dfb0b4f6 --- /dev/null +++ b/drivers/w1/slaves/w1_ds2781.h @@ -0,0 +1,134 @@ +/* + * 1-Wire implementation for the ds2780 chip + * + * Author: Renata Sayakhova <renata@oktetlabs.ru> + * + * Based on w1-ds2760 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef _W1_DS2781_H +#define _W1_DS2781_H + +/* Function commands */ +#define W1_DS2781_READ_DATA 0x69 +#define W1_DS2781_WRITE_DATA 0x6C +#define W1_DS2781_COPY_DATA 0x48 +#define W1_DS2781_RECALL_DATA 0xB8 +#define W1_DS2781_LOCK 0x6A + +/* Register map */ +/* Register 0x00 Reserved */ +#define DS2781_STATUS 0x01 +#define DS2781_RAAC_MSB 0x02 +#define DS2781_RAAC_LSB 0x03 +#define DS2781_RSAC_MSB 0x04 +#define DS2781_RSAC_LSB 0x05 +#define DS2781_RARC 0x06 +#define DS2781_RSRC 0x07 +#define DS2781_IAVG_MSB 0x08 +#define DS2781_IAVG_LSB 0x09 +#define DS2781_TEMP_MSB 0x0A +#define DS2781_TEMP_LSB 0x0B +#define DS2781_VOLT_MSB 0x0C +#define DS2781_VOLT_LSB 0x0D +#define DS2781_CURRENT_MSB 0x0E +#define DS2781_CURRENT_LSB 0x0F +#define DS2781_ACR_MSB 0x10 +#define DS2781_ACR_LSB 0x11 +#define DS2781_ACRL_MSB 0x12 +#define DS2781_ACRL_LSB 0x13 +#define DS2781_AS 0x14 +#define DS2781_SFR 0x15 +#define DS2781_FULL_MSB 0x16 +#define DS2781_FULL_LSB 0x17 +#define DS2781_AE_MSB 0x18 +#define DS2781_AE_LSB 0x19 +#define DS2781_SE_MSB 0x1A +#define DS2781_SE_LSB 0x1B +/* Register 0x1C - 0x1E Reserved */ +#define DS2781_EEPROM 0x1F +#define DS2781_EEPROM_BLOCK0_START 0x20 +/* Register 0x20 - 0x2F User EEPROM */ +#define DS2781_EEPROM_BLOCK0_END 0x2F +/* Register 0x30 - 0x5F Reserved */ +#define DS2781_EEPROM_BLOCK1_START 0x60 +#define DS2781_CONTROL 0x60 +#define DS2781_AB 0x61 +#define DS2781_AC_MSB 0x62 +#define DS2781_AC_LSB 0x63 +#define DS2781_VCHG 0x64 +#define DS2781_IMIN 0x65 +#define DS2781_VAE 0x66 +#define DS2781_IAE 0x67 +#define DS2781_AE_40 0x68 +#define DS2781_RSNSP 0x69 +#define DS2781_FULL_40_MSB 0x6A +#define DS2781_FULL_40_LSB 0x6B +#define DS2781_FULL_4_SLOPE 0x6C +#define DS2781_FULL_3_SLOPE 0x6D +#define DS2781_FULL_2_SLOPE 0x6E +#define DS2781_FULL_1_SLOPE 0x6F +#define DS2781_AE_4_SLOPE 0x70 +#define DS2781_AE_3_SLOPE 0x71 +#define DS2781_AE_2_SLOPE 0x72 +#define DS2781_AE_1_SLOPE 0x73 +#define DS2781_SE_4_SLOPE 0x74 +#define DS2781_SE_3_SLOPE 0x75 +#define DS2781_SE_2_SLOPE 0x76 +#define DS2781_SE_1_SLOPE 0x77 +#define DS2781_RSGAIN_MSB 0x78 +#define DS2781_RSGAIN_LSB 0x79 +#define DS2781_RSTC 0x7A +#define DS2781_COB 0x7B +#define DS2781_TBP34 0x7C +#define DS2781_TBP23 0x7D +#define DS2781_TBP12 0x7E +#define DS2781_EEPROM_BLOCK1_END 0x7F +/* Register 0x7D - 0xFF Reserved */ + +#define DS2781_FSGAIN_MSB 0xB0 +#define DS2781_FSGAIN_LSB 0xB1 + +/* Number of valid register addresses */ +#define DS2781_DATA_SIZE 0xB2 + +/* Status register bits */ +#define DS2781_STATUS_CHGTF (1 << 7) +#define DS2781_STATUS_AEF (1 << 6) +#define DS2781_STATUS_SEF (1 << 5) +#define DS2781_STATUS_LEARNF (1 << 4) +/* Bit 3 Reserved */ +#define DS2781_STATUS_UVF (1 << 2) +#define DS2781_STATUS_PORF (1 << 1) +/* Bit 0 Reserved */ + +/* Control register bits */ +/* Bit 7 Reserved */ +#define DS2781_CONTROL_NBEN (1 << 7) +#define DS2781_CONTROL_UVEN (1 << 6) +#define DS2781_CONTROL_PMOD (1 << 5) +#define DS2781_CONTROL_RNAOP (1 << 4) +#define DS1781_CONTROL_UVTH (1 << 3) +/* Bit 0 - 2 Reserved */ + +/* Special feature register bits */ +/* Bit 1 - 7 Reserved */ +#define DS2781_SFR_PIOSC (1 << 0) + +/* EEPROM register bits */ +#define DS2781_EEPROM_EEC (1 << 7) +#define DS2781_EEPROM_LOCK (1 << 6) +/* Bit 2 - 6 Reserved */ +#define DS2781_EEPROM_BL1 (1 << 1) +#define DS2781_EEPROM_BL0 (1 << 0) + +extern int w1_ds2781_io(struct device *dev, char *buf, int addr, size_t count, + int io); +extern int w1_ds2781_eeprom_cmd(struct device *dev, int addr, int cmd); + +#endif /* !_W1_DS2781_H */ diff --git a/drivers/w1/slaves/w1_ds28e04.c b/drivers/w1/slaves/w1_ds28e04.c new file mode 100644 index 00000000000..365d6dff21d --- /dev/null +++ b/drivers/w1/slaves/w1_ds28e04.c @@ -0,0 +1,442 @@ +/* + * w1_ds28e04.c - w1 family 1C (DS28E04) driver + * + * Copyright (c) 2012 Markus Franke <franke.m@sebakmt.com> + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/crc16.h> +#include <linux/uaccess.h> + +#define CRC16_INIT 0 +#define CRC16_VALID 0xb001 + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Markus Franke <franke.m@sebakmt.com>, <franm@hrz.tu-chemnitz.de>"); +MODULE_DESCRIPTION("w1 family 1C driver for DS28E04, 4kb EEPROM and PIO"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS28E04)); + +/* Allow the strong pullup to be disabled, but default to enabled. + * If it was disabled a parasite powered device might not get the required + * current to copy the data from the scratchpad to EEPROM. If it is enabled + * parasite powered devices have a better chance of getting the current + * required. + */ +static int w1_strong_pullup = 1; +module_param_named(strong_pullup, w1_strong_pullup, int, 0); + +/* enable/disable CRC checking on DS28E04-100 memory accesses */ +static char w1_enable_crccheck = 1; + +#define W1_EEPROM_SIZE 512 +#define W1_PAGE_COUNT 16 +#define W1_PAGE_SIZE 32 +#define W1_PAGE_BITS 5 +#define W1_PAGE_MASK 0x1F + +#define W1_F1C_READ_EEPROM 0xF0 +#define W1_F1C_WRITE_SCRATCH 0x0F +#define W1_F1C_READ_SCRATCH 0xAA +#define W1_F1C_COPY_SCRATCH 0x55 +#define W1_F1C_ACCESS_WRITE 0x5A + +#define W1_1C_REG_LOGIC_STATE 0x220 + +struct w1_f1C_data { + u8 memory[W1_EEPROM_SIZE]; + u32 validcrc; +}; + +/** + * Check the file size bounds and adjusts count as needed. + * This would not be needed if the file size didn't reset to 0 after a write. + */ +static inline size_t w1_f1C_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; +} + +static int w1_f1C_refresh_block(struct w1_slave *sl, struct w1_f1C_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_F1C_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; +} + +static int w1_f1C_read(struct w1_slave *sl, int addr, int len, char *data) +{ + u8 wrbuf[3]; + + /* read directly from the EEPROM */ + if (w1_reset_select_slave(sl)) + return -EIO; + + wrbuf[0] = W1_F1C_READ_EEPROM; + wrbuf[1] = addr & 0xff; + wrbuf[2] = addr >> 8; + + w1_write_block(sl->master, wrbuf, sizeof(wrbuf)); + return w1_read_block(sl->master, data, len); +} + +static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + struct w1_f1C_data *data = sl->family_data; + int i, min_page, max_page; + + count = w1_f1C_fix_count(off, count, W1_EEPROM_SIZE); + if (count == 0) + return 0; + + mutex_lock(&sl->master->mutex); + + if (w1_enable_crccheck) { + min_page = (off >> W1_PAGE_BITS); + max_page = (off + count - 1) >> W1_PAGE_BITS; + for (i = min_page; i <= max_page; i++) { + if (w1_f1C_refresh_block(sl, data, i)) { + count = -EIO; + goto out_up; + } + } + memcpy(buf, &data->memory[off], count); + } else { + count = w1_f1C_read(sl, off, count, buf); + } + +out_up: + mutex_unlock(&sl->master->mutex); + + return count; +} + +/** + * Writes to the scratchpad and reads it back for verification. + * Then copies the scratchpad to EEPROM. + * The data must be on one page. + * 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_f1C_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; + unsigned int tm = 10; + int i; + struct w1_f1C_data *f1C = sl->family_data; + + /* Write the data to the scratchpad */ + if (w1_reset_select_slave(sl)) + return -1; + + wrbuf[0] = W1_F1C_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_F1C_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_F1C_COPY_SCRATCH; + wrbuf[3] = es; + + for (i = 0; i < sizeof(wrbuf); ++i) { + /* issue 10ms strong pullup (or delay) on the last byte + for writing the data from the scratchpad to EEPROM */ + if (w1_strong_pullup && i == sizeof(wrbuf)-1) + w1_next_pullup(sl->master, tm); + + w1_write_8(sl->master, wrbuf[i]); + } + + if (!w1_strong_pullup) + msleep(tm); + + if (w1_enable_crccheck) { + /* invalidate cached data */ + f1C->validcrc &= ~(1 << (addr >> W1_PAGE_BITS)); + } + + /* Reset the bus to wake up the EEPROM (this may not be needed) */ + w1_reset_bus(sl->master); + + return 0; +} + +static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) + +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + int addr, len, idx; + + count = w1_f1C_fix_count(off, count, W1_EEPROM_SIZE); + if (count == 0) + return 0; + + if (w1_enable_crccheck) { + /* can only write full blocks in cached mode */ + if ((off & W1_PAGE_MASK) || (count & W1_PAGE_MASK)) { + dev_err(&sl->dev, "invalid offset/count off=%d cnt=%zd\n", + (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; + } + } + } + + mutex_lock(&sl->master->mutex); + + /* 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_f1C_write(sl, addr, len, &buf[idx]) < 0) { + count = -EIO; + goto out_up; + } + idx += len; + } + +out_up: + mutex_unlock(&sl->master->mutex); + + return count; +} + +static BIN_ATTR_RW(eeprom, W1_EEPROM_SIZE); + +static ssize_t pio_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, + size_t count) + +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + int ret; + + /* check arguments */ + if (off != 0 || count != 1 || buf == NULL) + return -EINVAL; + + mutex_lock(&sl->master->mutex); + ret = w1_f1C_read(sl, W1_1C_REG_LOGIC_STATE, count, buf); + mutex_unlock(&sl->master->mutex); + + return ret; +} + +static ssize_t pio_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, + size_t count) + +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + u8 wrbuf[3]; + u8 ack; + + /* check arguments */ + if (off != 0 || count != 1 || buf == NULL) + return -EINVAL; + + mutex_lock(&sl->master->mutex); + + /* Write the PIO data */ + if (w1_reset_select_slave(sl)) { + mutex_unlock(&sl->master->mutex); + return -1; + } + + /* set bit 7..2 to value '1' */ + *buf = *buf | 0xFC; + + wrbuf[0] = W1_F1C_ACCESS_WRITE; + wrbuf[1] = *buf; + wrbuf[2] = ~(*buf); + w1_write_block(sl->master, wrbuf, 3); + + w1_read_block(sl->master, &ack, sizeof(ack)); + + mutex_unlock(&sl->master->mutex); + + /* check for acknowledgement */ + if (ack != 0xAA) + return -EIO; + + return count; +} + +static BIN_ATTR_RW(pio, 1); + +static ssize_t crccheck_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + if (put_user(w1_enable_crccheck + 0x30, buf)) + return -EFAULT; + + return sizeof(w1_enable_crccheck); +} + +static ssize_t crccheck_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + char val; + + if (count != 1 || !buf) + return -EINVAL; + + if (get_user(val, buf)) + return -EFAULT; + + /* convert to decimal */ + val = val - 0x30; + if (val != 0 && val != 1) + return -EINVAL; + + /* set the new value */ + w1_enable_crccheck = val; + + return sizeof(w1_enable_crccheck); +} + +static DEVICE_ATTR_RW(crccheck); + +static struct attribute *w1_f1C_attrs[] = { + &dev_attr_crccheck.attr, + NULL, +}; + +static struct bin_attribute *w1_f1C_bin_attrs[] = { + &bin_attr_eeprom, + &bin_attr_pio, + NULL, +}; + +static const struct attribute_group w1_f1C_group = { + .attrs = w1_f1C_attrs, + .bin_attrs = w1_f1C_bin_attrs, +}; + +static const struct attribute_group *w1_f1C_groups[] = { + &w1_f1C_group, + NULL, +}; + +static int w1_f1C_add_slave(struct w1_slave *sl) +{ + struct w1_f1C_data *data = NULL; + + if (w1_enable_crccheck) { + data = kzalloc(sizeof(struct w1_f1C_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + sl->family_data = data; + } + + return 0; +} + +static void w1_f1C_remove_slave(struct w1_slave *sl) +{ + kfree(sl->family_data); + sl->family_data = NULL; +} + +static struct w1_family_ops w1_f1C_fops = { + .add_slave = w1_f1C_add_slave, + .remove_slave = w1_f1C_remove_slave, + .groups = w1_f1C_groups, +}; + +static struct w1_family w1_family_1C = { + .fid = W1_FAMILY_DS28E04, + .fops = &w1_f1C_fops, +}; + +static int __init w1_f1C_init(void) +{ + return w1_register_family(&w1_family_1C); +} + +static void __exit w1_f1C_fini(void) +{ + w1_unregister_family(&w1_family_1C); +} + +module_init(w1_f1C_init); +module_exit(w1_f1C_fini); diff --git a/drivers/w1/slaves/w1_smem.c b/drivers/w1/slaves/w1_smem.c index cc8c02e9259..ed4c87506de 100644 --- a/drivers/w1/slaves/w1_smem.c +++ b/drivers/w1/slaves/w1_smem.c @@ -1,7 +1,7 @@ /* * w1_smem.c * - * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru> + * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net> * * * This program is free software; you can redistribute it and/or modify @@ -32,8 +32,10 @@ #include "../w1_family.h" MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>"); +MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>"); MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, 64bit memory family."); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_SMEM_01)); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_SMEM_81)); static struct w1_family w1_smem_family_01 = { .fid = W1_FAMILY_SMEM_01, diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index fb28acaeed6..1f11a20a8ab 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -1,7 +1,7 @@ /* * w1_therm.c * - * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru> + * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net> * * * This program is free software; you can redistribute it and/or modify @@ -27,6 +27,7 @@ #include <linux/sched.h> #include <linux/device.h> #include <linux/types.h> +#include <linux/slab.h> #include <linux/delay.h> #include "../w1.h" @@ -34,39 +35,59 @@ #include "../w1_family.h" MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>"); +MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>"); MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, temperature family."); - -static u8 bad_roms[][9] = { - {0xaa, 0x00, 0x4b, 0x46, 0xff, 0xff, 0x0c, 0x10, 0x87}, - {} - }; - -static ssize_t w1_therm_read_bin(struct kobject *, struct bin_attribute *, - char *, loff_t, size_t); - -static struct bin_attribute w1_therm_bin_attr = { - .attr = { - .name = "w1_slave", - .mode = S_IRUGO, - }, - .size = W1_SLAVE_DATA_SIZE, - .read = w1_therm_read_bin, -}; +MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS18S20)); +MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS1822)); +MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS18B20)); +MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS1825)); +MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS28EA00)); + +/* Allow the strong pullup to be disabled, but default to enabled. + * If it was disabled a parasite powered device might not get the require + * current to do a temperature conversion. If it is enabled parasite powered + * devices have a better chance of getting the current required. + * In case the parasite power-detection is not working (seems to be the case + * for some DS18S20) the strong pullup can also be forced, regardless of the + * power state of the devices. + * + * Summary of options: + * - strong_pullup = 0 Disable strong pullup completely + * - strong_pullup = 1 Enable automatic strong pullup detection + * - strong_pullup = 2 Force strong pullup + */ +static int w1_strong_pullup = 1; +module_param_named(strong_pullup, w1_strong_pullup, int, 0); static int w1_therm_add_slave(struct w1_slave *sl) { - return sysfs_create_bin_file(&sl->dev.kobj, &w1_therm_bin_attr); + sl->family_data = kzalloc(9, GFP_KERNEL); + if (!sl->family_data) + return -ENOMEM; + return 0; } static void w1_therm_remove_slave(struct w1_slave *sl) { - sysfs_remove_bin_file(&sl->dev.kobj, &w1_therm_bin_attr); + kfree(sl->family_data); + sl->family_data = NULL; } +static ssize_t w1_slave_show(struct device *device, + struct device_attribute *attr, char *buf); + +static DEVICE_ATTR_RO(w1_slave); + +static struct attribute *w1_therm_attrs[] = { + &dev_attr_w1_slave.attr, + NULL, +}; +ATTRIBUTE_GROUPS(w1_therm); + static struct w1_family_ops w1_therm_fops = { .add_slave = w1_therm_add_slave, .remove_slave = w1_therm_remove_slave, + .groups = w1_therm_groups, }; static struct w1_family w1_therm_family_DS18S20 = { @@ -84,6 +105,16 @@ static struct w1_family w1_therm_family_DS1822 = { .fops = &w1_therm_fops, }; +static struct w1_family w1_therm_family_DS28EA00 = { + .fid = W1_THERM_DS28EA00, + .fops = &w1_therm_fops, +}; + +static struct w1_family w1_therm_family_DS1825 = { + .fid = W1_THERM_DS1825, + .fops = &w1_therm_fops, +}; + struct w1_therm_family_converter { u8 broken; @@ -109,13 +140,20 @@ static struct w1_therm_family_converter w1_therm_families[] = { .f = &w1_therm_family_DS18B20, .convert = w1_DS18B20_convert_temp }, + { + .f = &w1_therm_family_DS28EA00, + .convert = w1_DS18B20_convert_temp + }, + { + .f = &w1_therm_family_DS1825, + .convert = w1_DS18B20_convert_temp + } }; static inline int w1_DS18B20_convert_temp(u8 rom[9]) { - s16 t = (rom[1] << 8) | rom[0]; - t = t*1000/16; - return t; + s16 t = le16_to_cpup((__le16 *)rom); + return t*1000/16; } static inline int w1_DS18S20_convert_temp(u8 rom[9]) @@ -149,58 +187,70 @@ static inline int w1_convert_temp(u8 rom[9], u8 fid) return 0; } -static int w1_therm_check_rom(u8 rom[9]) -{ - int i; - - for (i=0; i<sizeof(bad_roms)/9; ++i) - if (!memcmp(bad_roms[i], rom, 9)) - return 1; - return 0; -} - -static ssize_t w1_therm_read_bin(struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static ssize_t w1_slave_show(struct device *device, + struct device_attribute *attr, char *buf) { - struct w1_slave *sl = kobj_to_w1_slave(kobj); + struct w1_slave *sl = dev_to_w1_slave(device); struct w1_master *dev = sl->master; - u8 rom[9], crc, verdict; + u8 rom[9], crc, verdict, external_power; int i, max_trying = 10; + ssize_t c = PAGE_SIZE; - mutex_lock(&sl->master->mutex); - - if (off > W1_SLAVE_DATA_SIZE) { - count = 0; - goto out; - } - if (off + count > W1_SLAVE_DATA_SIZE) { - count = 0; - goto out; - } + i = mutex_lock_interruptible(&dev->bus_mutex); + if (i != 0) + return i; - memset(buf, 0, count); memset(rom, 0, sizeof(rom)); - count = 0; - verdict = 0; - crc = 0; - while (max_trying--) { + + verdict = 0; + crc = 0; + if (!w1_reset_select_slave(sl)) { int count = 0; unsigned int tm = 750; + unsigned long sleep_rem; + + w1_write_8(dev, W1_READ_PSUPPLY); + external_power = w1_read_8(dev); + + if (w1_reset_select_slave(sl)) + continue; + + /* 750ms strong pullup (or delay) after the convert */ + if (w1_strong_pullup == 2 || + (!external_power && w1_strong_pullup)) + w1_next_pullup(dev, tm); w1_write_8(dev, W1_CONVERT_TEMP); - msleep(tm); + if (external_power) { + mutex_unlock(&dev->bus_mutex); + + sleep_rem = msleep_interruptible(tm); + if (sleep_rem != 0) + return -EINTR; + + i = mutex_lock_interruptible(&dev->bus_mutex); + if (i != 0) + return i; + } else if (!w1_strong_pullup) { + sleep_rem = msleep_interruptible(tm); + if (sleep_rem != 0) { + mutex_unlock(&dev->bus_mutex); + return -EINTR; + } + } if (!w1_reset_select_slave(sl)) { w1_write_8(dev, W1_READ_SCRATCHPAD); if ((count = w1_read_block(dev, rom, 9)) != 9) { - dev_warn(&dev->dev, "w1_read_block() returned %d instead of 9.\n", count); + dev_warn(device, "w1_read_block() " + "returned %u instead of 9.\n", + count); } crc = w1_calc_crc8(rom, 8); @@ -210,27 +260,28 @@ static ssize_t w1_therm_read_bin(struct kobject *kobj, } } - if (!w1_therm_check_rom(rom)) + if (verdict) break; } for (i = 0; i < 9; ++i) - count += sprintf(buf + count, "%02x ", rom[i]); - count += sprintf(buf + count, ": crc=%02x %s\n", + c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", rom[i]); + c -= snprintf(buf + PAGE_SIZE - c, c, ": crc=%02x %s\n", crc, (verdict) ? "YES" : "NO"); if (verdict) - memcpy(sl->rom, rom, sizeof(sl->rom)); + memcpy(sl->family_data, rom, sizeof(rom)); else - dev_warn(&dev->dev, "18S20 doesn't respond to CONVERT_TEMP.\n"); + dev_warn(device, "Read failed CRC check\n"); for (i = 0; i < 9; ++i) - count += sprintf(buf + count, "%02x ", sl->rom[i]); + c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", + ((u8 *)sl->family_data)[i]); - count += sprintf(buf + count, "t=%d\n", w1_convert_temp(rom, sl->family->fid)); -out: - mutex_unlock(&dev->mutex); + c -= snprintf(buf + PAGE_SIZE - c, c, "t=%d\n", + w1_convert_temp(rom, sl->family->fid)); + mutex_unlock(&dev->bus_mutex); - return count; + return PAGE_SIZE - c; } static int __init w1_therm_init(void) |
