diff options
Diffstat (limited to 'drivers/w1')
-rw-r--r-- | drivers/w1/masters/mxc_w1.c | 177 | ||||
-rw-r--r-- | drivers/w1/slaves/Kconfig | 23 | ||||
-rw-r--r-- | drivers/w1/slaves/Makefile | 2 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds2438.c | 204 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds2438.h | 125 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds2751.c | 320 | ||||
-rw-r--r-- | drivers/w1/w1_family.h | 2 |
7 files changed, 852 insertions, 1 deletions
diff --git a/drivers/w1/masters/mxc_w1.c b/drivers/w1/masters/mxc_w1.c index a3b6a74c67a7..adc643827007 100644 --- a/drivers/w1/masters/mxc_w1.c +++ b/drivers/w1/masters/mxc_w1.c @@ -1,5 +1,5 @@ /* - * Copyright 2005-2008 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2005-2010 Freescale Semiconductor, Inc. All Rights Reserved. * Copyright 2008 Luotao Fu, kernel@pengutronix.de * * This program is free software; you can redistribute it and/or @@ -24,6 +24,7 @@ #include <linux/slab.h> #include <linux/delay.h> #include <linux/io.h> +#include <linux/fsl_devices.h> #include "../w1.h" #include "../w1_int.h" @@ -44,6 +45,8 @@ #define MXC_W1_INTERRUPT 0x0A #define MXC_W1_INTERRUPT_EN 0x0C +static DECLARE_COMPLETION(transmit_done); + struct mxc_w1_device { void __iomem *regs; unsigned int clkdiv; @@ -103,10 +106,155 @@ static u8 mxc_w1_ds2_touch_bit(void *data, u8 bit) return ((__raw_readb(ctrl_addr)) >> 3) & 0x1; } +static void mxc_w1_ds2_write_byte(void *data, u8 byte) +{ + struct mxc_w1_device *dev = (struct mxc_w1_device *)data; + INIT_COMPLETION(transmit_done); + __raw_writeb(byte, (dev->regs + MXC_W1_TXRX)); + __raw_writeb(0x10, (dev->regs + MXC_W1_INTERRUPT_EN)); + wait_for_completion(&transmit_done); +} +static u8 mxc_w1_ds2_read_byte(void *data) +{ + volatile u8 reg_val; + struct mxc_w1_device *dev = (struct mxc_w1_device *)data; + mxc_w1_ds2_write_byte(data, 0xFF); + reg_val = __raw_readb((dev->regs + MXC_W1_TXRX)); + return reg_val; +} +static u8 mxc_w1_read_byte(void *data) +{ + volatile u8 reg_val; + struct mxc_w1_device *dev = (struct mxc_w1_device *)data; + reg_val = __raw_readb((dev->regs + MXC_W1_TXRX)); + return reg_val; +} +static irqreturn_t w1_interrupt_handler(int irq, void *data) +{ + u8 reg_val; + irqreturn_t ret = IRQ_NONE; + struct mxc_w1_device *dev = (struct mxc_w1_device *)data; + reg_val = __raw_readb((dev->regs + MXC_W1_INTERRUPT)); + if ((reg_val & 0x10)) { + complete(&transmit_done); + reg_val = __raw_readb((dev->regs + MXC_W1_TXRX)); + ret = IRQ_HANDLED; + } + return ret; +} +void search_ROM_accelerator(void *data, struct w1_master *master, u8 search_type, + w1_slave_found_callback cb) +{ + u64 rn[2], last_rn[2], rn2[2]; + u64 rn1, rom_id, temp, temp1; + int i, j, z, w, last_zero, loop; + u8 bit, reg_val, bit2; + u8 byte, byte1; + int disc, prev_disc, last_disc; + struct mxc_w1_device *dev = (struct mxc_w1_device *)data; + last_rn[0] = 0; + last_rn[1] = 0; + rom_id = 0; + prev_disc = 0; + loop = 0; + disc = -1; + last_disc = 0; + last_zero = 0; + while (!last_zero) { + /* + * Reset bus and all 1-wire device state machines + * so they can respond to our requests. + * + * Return 0 - device(s) present, 1 - no devices present. + */ + if (mxc_w1_ds2_reset_bus(data)) { + pr_debug("No devices present on the wire.\n"); + break; + } + rn[0] = 0; + rn[1] = 0; + __raw_writeb(0x80, (dev->regs + MXC_W1_CONTROL)); + mdelay(1); + mxc_w1_ds2_write_byte(data, 0xF0); + __raw_writeb(0x02, (dev->regs + MXC_W1_COMMAND)); + memcpy(rn2, last_rn, 16); + z = 0; + w = 0; + for (i = 0; i < 16; i++) { + reg_val = rn2[z] >> (8 * w); + mxc_w1_ds2_write_byte(data, reg_val); + reg_val = mxc_w1_read_byte(data); + if ((reg_val & 0x3) == 0x3) { + pr_debug("Device is Not Responding\n"); + break; + } + for (j = 0; j < 8; j += 2) { + byte = 0xFF; + byte1 = 1; + byte ^= byte1 << j; + bit = (reg_val >> j) & 0x1; + bit2 = (reg_val >> j); + if (bit) { + prev_disc = disc; + disc = 8 * i + j; + reg_val &= byte; + } + } + rn1 = 0; + rn1 = reg_val; + rn[z] |= rn1 << (8 * w); + w++; + if (i == 7) { + z++; + w = 0; + } + } + if ((disc == -1) || (disc == prev_disc)) + last_zero = 1; + if (disc == last_disc) + disc = prev_disc; + z = 0; + rom_id = 0; + for (i = 0, j = 1; i < 64; i++) { + temp = 0; + temp = (rn[z] >> j) & 0x1; + rom_id |= (temp << i); + j += 2; + if (i == 31) { + z++; + j = 1; + } + + } + if (disc > 63) { + last_rn[0] = rn[0]; + temp1 = rn[1]; + loop = disc % 64; + temp = 1; + temp1 |= (temp << (loop + 1)) - 1; + temp1 |= (temp << (loop + 1)); + last_rn[1] = temp1; + + } else { + last_rn[1] = 0; + temp1 = rn[0]; + temp = 1; + temp1 |= (temp << (loop + 1)) - 1; + temp1 |= (temp << (loop + 1)); + last_rn[0] = temp1; + } + last_disc = disc; + cb(master, rom_id); + } +} + static int __devinit mxc_w1_probe(struct platform_device *pdev) { struct mxc_w1_device *mdev; + struct mxc_w1_config *data = + (struct mxc_w1_config *)pdev->dev.platform_data; struct resource *res; + int irq = 0; int err = 0; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -144,6 +292,22 @@ static int __devinit mxc_w1_probe(struct platform_device *pdev) mdev->bus_master.data = mdev; mdev->bus_master.reset_bus = mxc_w1_ds2_reset_bus; mdev->bus_master.touch_bit = mxc_w1_ds2_touch_bit; + if (data->search_rom_accelerator) { + mdev->bus_master.write_byte = &mxc_w1_ds2_write_byte; + mdev->bus_master.read_byte = &mxc_w1_ds2_read_byte; + mdev->bus_master.search = &search_ROM_accelerator; + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + err = -ENOENT; + goto failed_irq; + } + err = request_irq(irq, w1_interrupt_handler, 0, "mxc_w1", mdev); + if (err) { + pr_debug("OWire:request_irq(%d) returned error %d\n", + irq, err); + goto failed_irq; + } + } err = w1_add_master_device(&mdev->bus_master); @@ -154,6 +318,9 @@ static int __devinit mxc_w1_probe(struct platform_device *pdev) return 0; failed_add: + if (irq) + free_irq(irq, mdev); +failed_irq: iounmap(mdev->regs); failed_ioremap: release_mem_region(res->start, resource_size(res)); @@ -171,6 +338,9 @@ static int __devexit mxc_w1_remove(struct platform_device *pdev) { struct mxc_w1_device *mdev = platform_get_drvdata(pdev); struct resource *res; + struct mxc_w1_config *data = + (struct mxc_w1_config *)pdev->dev.platform_data; + int irq; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -178,6 +348,11 @@ static int __devexit mxc_w1_remove(struct platform_device *pdev) iounmap(mdev->regs); release_mem_region(res->start, resource_size(res)); + + irq = platform_get_irq(pdev, 0); + if ((irq >= 0) && (data->search_rom_accelerator)) + free_irq(irq, mdev); + clk_disable(mdev->clk); clk_put(mdev->clk); diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index 1f51366417b9..58f3e64e600a 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -22,12 +22,35 @@ config W1_SLAVE_DS2431 Say Y here if you want to use a 1-wire 1kb EEPROM family device (DS2431) +config W1_SLAVE_DS2751 + tristate "Battery Level sensing support (DS2751)" + depends on W1 + help + Say Y here if you want to use a 1-wire + battery level sensing device (DS2751). + +config W1_SLAVE_DS2751_CRC + bool "Protect DS2751 data with a CRC16" + depends on W1_SLAVE_DS2751 + select CRC16 + help + Say Y here to protect DS2751 data with a CRC16. + Each block has 30 bytes of data and a two byte CRC16. + Full block writes are only allowed if the CRC is valid. + config W1_SLAVE_DS2433 tristate "4kb EEPROM family support (DS2433)" help Say Y here if you want to use a 1-wire 4kb EEPROM family device (DS2433). +config W1_SLAVE_DS2438 + tristate "Smart Battery Monitor (DS2438)" + depends on W1 + help + Say Y here if you want to use a 1-wire + Smart Battery Monitor family device (DS2438). + config W1_SLAVE_DS2433_CRC bool "Protect DS2433 data with a CRC16" depends on W1_SLAVE_DS2433 diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index f1f51f19b129..fd53f53d8681 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile @@ -8,3 +8,5 @@ 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_BQ27000) += w1_bq27000.o +obj-$(CONFIG_W1_SLAVE_DS2751) += w1_ds2751.o +obj-$(CONFIG_W1_SLAVE_DS2438) += w1_ds2438.o diff --git a/drivers/w1/slaves/w1_ds2438.c b/drivers/w1/slaves/w1_ds2438.c new file mode 100644 index 000000000000..4212117d9424 --- /dev/null +++ b/drivers/w1/slaves/w1_ds2438.c @@ -0,0 +1,204 @@ +/* + * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. + */ +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/idr.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" +#include "w1_ds2438.h" + +/* W1 slave DS2438 famliy operations */ +int w1_ds2438_read_page(struct device *dev, u8 page, u8 *buf) +{ + struct w1_slave *slave = container_of(dev, struct w1_slave, dev); + if ((page >= DS2438_PAGE_NUM) || (buf == NULL)) + return -EINVAL; + + mutex_lock(&slave->master->mutex); + if (!w1_reset_select_slave(slave)) { + w1_write_8(slave->master, W1_READ_SCRATCHPAD); + w1_write_8(slave->master, page); + w1_read_block(slave->master, buf, DS2438_PAGE_SIZE); + } + mutex_unlock(&slave->master->mutex); + return 0; +} +EXPORT_SYMBOL(w1_ds2438_read_page); + +int w1_ds2438_write_page(struct device *dev, u8 page, u8 *buf) +{ + struct w1_slave *slave = container_of(dev, struct w1_slave, dev); + if ((page >= DS2438_PAGE_NUM) || (buf == NULL)) + return -EINVAL; + + mutex_lock(&slave->master->mutex); + if (!w1_reset_select_slave(slave)) { + w1_write_8(slave->master, DS2438_WRITE_SCRATCHPAD); + w1_write_8(slave->master, page); + w1_write_block(slave->master, buf, DS2438_PAGE_SIZE); + } + mutex_unlock(&slave->master->mutex); + return 0; +} +EXPORT_SYMBOL(w1_ds2438_write_page); + +int w1_ds2438_command(struct device *dev, u8 command, u8 data) +{ + struct w1_slave *slave = container_of(dev, struct w1_slave, dev); + + mutex_lock(&slave->master->mutex); + if (!w1_reset_select_slave(slave)) { + w1_write_8(slave->master, command); + switch (command) { + case DS2438_COPY_SCRATCHPAD: + case DS2438_RECALL_MEMORY: + w1_write_8(slave->master, data); + } + } + mutex_unlock(&slave->master->mutex); + return 0; +} +EXPORT_SYMBOL(w1_ds2438_command); + +int w1_ds2438_drain_sram(struct device *dev, u8 page) +{ + return w1_ds2438_command(dev, DS2438_COPY_SCRATCHPAD, page); +} +EXPORT_SYMBOL(w1_ds2438_drain_sram); + +int w1_ds2438_load_sram(struct device *dev, u8 page) +{ + return w1_ds2438_command(dev, DS2438_RECALL_MEMORY, page); +} +EXPORT_SYMBOL(w1_ds2438_load_sram); + +static DEFINE_IDR(bat_idr); +static DEFINE_MUTEX(bat_idr_lock); + +static int new_bat_id(void) +{ + int ret; + + while (1) { + int id; + + ret = idr_pre_get(&bat_idr, GFP_KERNEL); + if (ret == 0) + return -ENOMEM; + + mutex_lock(&bat_idr_lock); + ret = idr_get_new(&bat_idr, NULL, &id); + mutex_unlock(&bat_idr_lock); + + if (ret == 0) { + ret = id & MAX_ID_MASK; + break; + } else if (ret == -EAGAIN) { + continue; + } else { + break; + } + } + + return ret; +} + +static void release_bat_id(int id) +{ + mutex_lock(&bat_idr_lock); + idr_remove(&bat_idr, id); + mutex_unlock(&bat_idr_lock); +} + +static int ds2438_add_slave(struct w1_slave *slave) +{ + int ret; + int id; + struct platform_device *pdev; + + id = new_bat_id(); + if (id < 0) { + ret = id; + goto noid; + } + + pdev = platform_device_alloc(DS2438_DEV_NAME, id); + if (!pdev) { + ret = -ENOMEM; + goto pdev_alloc_failed; + } + pdev->dev.parent = &slave->dev; + + ret = platform_device_add(pdev); + if (ret) + goto pdev_add_failed; + + dev_set_drvdata(&slave->dev, pdev); + goto success; + +pdev_add_failed: + platform_device_unregister(pdev); +pdev_alloc_failed: + release_bat_id(id); +noid: +success: + return ret; +} + +static void ds2438_remove_slave(struct w1_slave *slave) +{ + struct platform_device *pdev = dev_get_drvdata(&slave->dev); + int id = pdev->id; + + platform_device_unregister(pdev); + release_bat_id(id); +} + +static struct w1_family_ops w1_ds2438_fops = { + .add_slave = ds2438_add_slave, + .remove_slave = ds2438_remove_slave, +}; + +static struct w1_family w1_family_ds2438 = { + .fid = W1_FAMILY_DS2438, + .fops = &w1_ds2438_fops, +}; + +static int __init w1_ds2438_init(void) +{ + pr_info("1-wire driver for the DS2438 smart battery monitor\n"); + idr_init(&bat_idr); + return w1_register_family(&w1_family_ds2438); +} + +static void __exit w1_ds2438_fini(void) +{ + w1_unregister_family(&w1_family_ds2438); + idr_destroy(&bat_idr); +} + +module_init(w1_ds2438_init); +module_exit(w1_ds2438_fini); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Freescale Semiconductors Inc"); +MODULE_DESCRIPTION("1-wire DS2438 family, smart battery monitor."); diff --git a/drivers/w1/slaves/w1_ds2438.h b/drivers/w1/slaves/w1_ds2438.h new file mode 100644 index 000000000000..b9fb73cafea4 --- /dev/null +++ b/drivers/w1/slaves/w1_ds2438.h @@ -0,0 +1,125 @@ +/* + * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. + */ +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef __W1_DS2438_H__ +#define __W1_DS2438_H__ + +#include <asm/types.h> + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/types.h> + +#define DS2438_DEV_NAME "ds2438-battery" + +#define DS2438_PAGE_SIZE 8 +#define DS2438_PAGE_NUM 8 + +#define DS2438_CONVERT_TEMP 0x44 +#define DS2438_CONVERT_VOLT 0xB4 +#define DS2438_WRITE_SCRATCHPAD 0x4E +#define DS2438_COPY_SCRATCHPAD 0x48 +#define DS2438_RECALL_MEMORY 0xB8 + +enum DS2438_PAGE { + PAGE0_CONTROL = 0, + PAGE1_ETM, + PAGE2_STAMP, + PAGE3_RESV3, + PAGE4_RESV4, + PAGE5_RESV5, + PAGE6_RESV6, + PAGE7_CCA, +}; + +enum DS2438_REG { + /* PAGE 0 */ + PAGE0_STAT_CTRL = 0, + PAGE0_TEMP_LSB = 1, + PAGE0_TEMP_MSB = 2, + PAGE0_VOLTAGE_LSB = 3, + PAGE0_VOLTAGE_MSB = 4, + PAGE0_CURRENT_LSB = 5, + PAGE0_CURRENT_MSB = 6, + PAGE0_THRESHOLD = 7, + + /* PAGE 1 */ + PAGE1_ETM_BYTE0 = 0, + PAGE1_ETM_BYTE1 = 1, + PAGE1_ETM_BYTE2 = 2, + PAGE1_ETM_BYTE3 = 3, + PAGE1_ICA = 4, + PAGE1_OFFSET_LSB = 5, + PAGE1_OFFSET_MSB = 6, + + /* PAGE 2 */ + PAGE2_DISCONNECT_BYTE0 = 0, + PAGE2_DISCONNECT_BYTE1 = 1, + PAGE2_DISCONNECT_BYTE2 = 2, + PAGE2_DISCONNECT_BYTE3 = 3, + PAGE2_END_CHARGE_BYTE0 = 4, + PAGE2_END_CHARGE_BYTE1 = 5, + PAGE2_END_CHARGE_BYTE2 = 6, + PAGE2_END_CHARGE_BYTE3 = 7, + + /* PAGE 3 */ + /* PAGE 4 */ + /* PAGE 5 */ + /* PAGE 6 */ + /* PAGE 7 */ + PAGE7_CCA_LSB = 4, + PAGE7_CCA_MSB = 5, + PAGE7_DCA_LSB = 6, + PAGE7_DCA_MSB = 7, +}; + +#define DS2438_CTRL_IAD (1 << 0) +#define DS2438_CTRL_CA (1 << 1) +#define DS2438_CTRL_EE (1 << 2) +#define DS2438_CTRL_AD (1 << 3) +#define DS2438_STAT_TB (1 << 4) +#define DS2438_STAT_NVB (1 << 5) +#define DS2438_STAT_ADB (1 << 6) + +struct ds2438_ops { + int (*read_page) (struct device *, u8, u8 *); + int (*read_byte) (struct device *, u8, u8, u8 *); + int (*read_halfword) (struct device *, u8, u8, u16 *); + int (*read_word) (struct device *, u8, u8, u32 *); + int (*write_page) (struct device *, u8, u8 *); + int (*write_byte) (struct device *, u8, u8, u8); + int (*write_halfword) (struct device *, u8, u8, u16); + int (*write_word) (struct device *, u8, u8, u32); + int (*drain_sram) (struct device *, u8); + int (*load_sram) (struct device *, u8); + int (*command) (struct device *, u8, u8); +}; + +static inline u16 ds2438_readw(u8 *raw) +{ + return ((*(raw + 1)) << 8) | (*raw); +} + +static inline void ds2438_writew(u8 *raw, u16 data) +{ + *raw++ = data & 0xFF; + *raw = (data >> 8) & 0xFF; +} + +extern int w1_ds2438_read_page(struct device *dev, u8 page, u8 *buf); +extern int w1_ds2438_write_page(struct device *dev, u8 page, u8 *buf); +extern int w1_ds2438_command(struct device *dev, u8 command, u8 data); +extern int w1_ds2438_drain_sram(struct device *dev, u8 page); +extern int w1_ds2438_load_sram(struct device *dev, u8 page); +#endif /* __W1_DS2438_H__ */ diff --git a/drivers/w1/slaves/w1_ds2751.c b/drivers/w1/slaves/w1_ds2751.c new file mode 100644 index 000000000000..9e5bf6faa4cb --- /dev/null +++ b/drivers/w1/slaves/w1_ds2751.c @@ -0,0 +1,320 @@ +/* + * Copyright 2005-2010 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +/* + * Implementation based on w1_ds2433.c + */ + +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/delay.h> +#ifdef CONFIG_W1_F51_CRC +#include <linux/crc16.h> + +#define CRC16_INIT 0 +#define CRC16_VALID 0xb001 + +#endif + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" + +#define W1_EEPROM_SIZE 32 +#define W1_PAGE_SIZE 32 +#define W1_PAGE_BITS 5 +#define W1_PAGE_MASK 0x1F + +#define W1_F51_TIME 300 + +#define W1_F51_READ_EEPROM 0xB8 +#define W1_F51_WRITE_SCRATCH 0x6C +#define W1_F51_READ_SCRATCH 0x69 +#define W1_F51_COPY_SCRATCH 0x48 +#define W1_STATUS_OFFSET 0x0001 +#define W1_EEPROM_OFFSET 0x0007 +#define W1_SPECIAL_OFFSET 0x0008 +#define W1_EEPROM_BLOCK_0 0x0020 +#define W1_EEPROM_BLOCK_1 0x0030 +#define W1_SRAM 0x0080 +struct w1_f51_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_f51_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; +} + +#ifdef CONFIG_W1_F51_CRC +static int w1_f51_refresh_block(struct w1_slave *sl, struct w1_f51_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_F51_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; +} +#endif /* CONFIG_W1_F51_CRC */ + +static ssize_t w1_f51_read_bin(struct kobject *kobj, char *buf, loff_t off, + size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); +#ifdef CONFIG_W1_F51_CRC + struct w1_f51_data *data = sl->family_data; + int i, min_page, max_page; +#else + u8 wrbuf[3]; +#endif + + count = w1_f51_fix_count(off, count, W1_EEPROM_SIZE) + if (count == 0) + return 0; + + mutex_lock(&sl->master->mutex); +#ifdef CONFIG_W1_F51_CRC + min_page = (off >> W1_PAGE_BITS); + max_page = (off + count - 1) >> W1_PAGE_BITS; + for (i = min_page; i <= max_page; i++) { + if (w1_f51_refresh_block(sl, data, i)) { + count = -EIO; + goto out_up; + } + } + memcpy(buf, &data->memory[off], count); + +#else /* CONFIG_W1_F51_CRC */ + + /* read directly from the EEPROM */ + if (w1_reset_select_slave(sl)) { + count = -EIO; + goto out_up; + } + off = (loff_t) W1_EEPROM_BLOCK_0; + wrbuf[0] = W1_F51_READ_EEPROM; + wrbuf[1] = off & 0xff; + wrbuf[2] = off >> 8; + w1_write_block(sl->master, wrbuf, 3); + if (w1_reset_select_slave(sl)) { + count = -EIO; + goto out_up; + } + + wrbuf[0] = W1_F51_READ_SCRATCH; + wrbuf[1] = off & 0xff; + wrbuf[2] = off >> 8; + w1_write_block(sl->master, wrbuf, 3); + w1_read_block(sl->master, buf, count); + +#endif /* CONFIG_W1_F51_CRC */ + + 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_f51_write(struct w1_slave *sl, int addr, int len, const u8 * data) +{ + u8 wrbuf[4]; + u8 rdbuf[W1_EEPROM_SIZE + 3]; + u8 es = (addr + len - 1) & 0x1f; + /* Write the data to the scratchpad */ + if (w1_reset_select_slave(sl)) + return -1; + wrbuf[0] = W1_F51_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; + wrbuf[0] = W1_F51_READ_SCRATCH; + w1_write_block(sl->master, wrbuf, 3); + w1_read_block(sl->master, rdbuf, len + 3); + /* Compare what was read against the data written */ + if (memcmp(data, &rdbuf[0], len) != 0) { + printk("Error reading the scratch Pad\n"); + return -1; + } + /* Copy the scratchpad to EEPROM */ + if (w1_reset_select_slave(sl)) + return -1; + wrbuf[0] = W1_F51_COPY_SCRATCH; + wrbuf[3] = es; + w1_write_block(sl->master, wrbuf, 4); + /* Sleep for 5 ms to wait for the write to complete */ + msleep(5); + + /* Reset the bus to wake up the EEPROM (this may not be needed) */ + w1_reset_bus(sl->master); + + return 0; +} + +static ssize_t w1_f51_write_bin(struct kobject *kobj, char *buf, loff_t off, + size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + int addr; + + count = w1_f51_fix_count(off, count, W1_EEPROM_SIZE); + if (count == 0) + return 0; + off = (loff_t) 0x0020; +#ifdef CONFIG_W1_F51_CRC + /* 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; + } + } +#endif /* CONFIG_W1_F51_CRC */ + + mutex_lock(&sl->master->mutex); + + /* Can only write data to one page at a time */ + addr = off; + if (w1_f51_write(sl, addr, count, buf) < 0) { + count = -EIO; + goto out_up; + } + + out_up: + mutex_unlock(&sl->master->mutex); + + return count; +} + +static struct bin_attribute w1_f51_bin_attr = { + .attr = { + .name = "eeprom", + .mode = S_IRUGO | S_IWUSR, + .owner = THIS_MODULE, + }, + .size = W1_EEPROM_SIZE, + .read = w1_f51_read_bin, + .write = w1_f51_write_bin, +}; + +static int w1_f51_add_slave(struct w1_slave *sl) +{ + int err; +#ifdef CONFIG_W1_F51_CRC + struct w1_f51_data *data; + data = kmalloc(sizeof(struct w1_f51_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + memset(data, 0, sizeof(struct w1_f51_data)); + sl->family_data = data; + +#endif /* CONFIG_W1_F51_CRC */ + + err = sysfs_create_bin_file(&sl->dev.kobj, &w1_f51_bin_attr); + +#ifdef CONFIG_W1_F51_CRC + if (err) + kfree(data); +#endif /* CONFIG_W1_F51_CRC */ + + return err; +} + +static void w1_f51_remove_slave(struct w1_slave *sl) +{ +#ifdef CONFIG_W1_F51_CRC + kfree(sl->family_data); + sl->family_data = NULL; +#endif /* CONFIG_W1_F51_CRC */ + sysfs_remove_bin_file(&sl->dev.kobj, &w1_f51_bin_attr); +} + +static struct w1_family_ops w1_f51_fops = { + .add_slave = w1_f51_add_slave, + .remove_slave = w1_f51_remove_slave, +}; + +static struct w1_family w1_family_51 = { + .fid = W1_EEPROM_DS2751, + .fops = &w1_f51_fops, +}; + +static int __init w1_f51_init(void) +{ + return w1_register_family(&w1_family_51); +} + +static void __exit w1_f51_fini(void) +{ + w1_unregister_family(&w1_family_51); +} + +module_init(w1_f51_init); +module_exit(w1_f51_fini); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Freescale Semiconductors Inc"); +MODULE_DESCRIPTION + ("w1 family 51 driver for DS2751, Battery Level Sensing Device"); diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h index 3ca1b9298f21..c541f9215765 100644 --- a/drivers/w1/w1_family.h +++ b/drivers/w1/w1_family.h @@ -32,8 +32,10 @@ #define W1_THERM_DS18S20 0x10 #define W1_THERM_DS1822 0x22 #define W1_EEPROM_DS2433 0x23 +#define W1_EEPROM_DS2751 0x51 #define W1_THERM_DS18B20 0x28 #define W1_EEPROM_DS2431 0x2D +#define W1_FAMILY_DS2438 0x26 #define W1_FAMILY_DS2760 0x30 #define MAXNAMELEN 32 |