summaryrefslogtreecommitdiff
path: root/drivers/w1
diff options
context:
space:
mode:
authorTodd Doucet <todd.doucet@timesys.com>2010-02-03 17:06:33 -0500
committerTodd Doucet <todd.doucet@timesys.com>2010-02-03 17:06:33 -0500
commitff238a4df84428befc55d49f58864dfc47ff853d (patch)
tree8796b828b396f5b7ed017904ff77b614dbf38677 /drivers/w1
parent4a6908a3a050aacc9c3a2f36b276b46c0629ad91 (diff)
Kernel as received from Digi for their Wi-Mx51 SoC running on their
CCWMX51JS carrier board.
Diffstat (limited to 'drivers/w1')
-rw-r--r--drivers/w1/masters/Kconfig6
-rw-r--r--drivers/w1/masters/Makefile2
-rw-r--r--drivers/w1/masters/mxc_w1.c449
-rw-r--r--drivers/w1/slaves/Kconfig28
-rw-r--r--drivers/w1/slaves/Makefile3
-rw-r--r--drivers/w1/slaves/w1_ds2438.c585
-rw-r--r--drivers/w1/slaves/w1_ds2438.h119
-rw-r--r--drivers/w1/slaves/w1_ds2751.c317
-rw-r--r--drivers/w1/w1_family.h2
9 files changed, 1511 insertions, 0 deletions
diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig
index 90616822cd20..f75d0d10c21f 100644
--- a/drivers/w1/masters/Kconfig
+++ b/drivers/w1/masters/Kconfig
@@ -34,6 +34,12 @@ config W1_MASTER_DS2482
This driver can also be built as a module. If so, the module
will be called ds2482.
+config W1_MASTER_MXC
+ tristate "Freescale MXC driver for 1-wire"
+ depends on W1 && ARCH_MXC
+ help
+ Say Y here to enable MXC 1-wire host
+
config W1_MASTER_DS1WM
tristate "Maxim DS1WM 1-wire busmaster"
depends on W1 && ARM && HAVE_CLK
diff --git a/drivers/w1/masters/Makefile b/drivers/w1/masters/Makefile
index bc4714a75f3a..c5a3e96fcbab 100644
--- a/drivers/w1/masters/Makefile
+++ b/drivers/w1/masters/Makefile
@@ -5,6 +5,8 @@
obj-$(CONFIG_W1_MASTER_MATROX) += matrox_w1.o
obj-$(CONFIG_W1_MASTER_DS2490) += ds2490.o
obj-$(CONFIG_W1_MASTER_DS2482) += ds2482.o
+obj-$(CONFIG_W1_MASTER_MXC) += mxc_w1.o
+
obj-$(CONFIG_W1_MASTER_DS1WM) += ds1wm.o
obj-$(CONFIG_W1_MASTER_GPIO) += w1-gpio.o
obj-$(CONFIG_HDQ_MASTER_OMAP) += omap_hdq.o
diff --git a/drivers/w1/masters/mxc_w1.c b/drivers/w1/masters/mxc_w1.c
new file mode 100644
index 000000000000..7c0e4a10d23d
--- /dev/null
+++ b/drivers/w1/masters/mxc_w1.c
@@ -0,0 +1,449 @@
+/*
+ * Copyright 2005-2009 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
+ */
+
+/*!
+ * @defgroup MXC_OWIRE MXC Driver for owire interface
+ */
+
+/*!
+ * @file mxc_w1.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC owire interface.
+ *
+ *
+ * @ingroup MXC_OWIRE
+ */
+
+/*!
+ * Include Files
+ */
+
+#include <asm/atomic.h>
+#include <asm/types.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/pci_ids.h>
+#include <linux/pci.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <mach/hardware.h>
+#include <asm/setup.h>
+
+#include "../w1.h"
+#include "../w1_int.h"
+#include "../w1_log.h"
+
+/*
+ * mxc function declarations
+ */
+
+static int __devinit mxc_w1_probe(struct platform_device *pdev);
+static int __devexit mxc_w1_remove(struct platform_device *pdev);
+static DECLARE_COMPLETION(transmit_done);
+extern void gpio_owire_active(void);
+extern void gpio_owire_inactive(void);
+
+/*
+ * MXC W1 Register offsets
+ */
+#define MXC_W1_CONTROL 0x00
+#define MXC_W1_TIME_DIVIDER 0x02
+#define MXC_W1_RESET 0x04
+#define MXC_W1_COMMAND 0x06
+#define MXC_W1_TXRX 0x08
+#define MXC_W1_INTERRUPT 0x0A
+#define MXC_W1_INTERRUPT_EN 0x0C
+DEFINE_SPINLOCK(w1_lock);
+
+/*!
+ * This structure contains pointers to callback functions.
+ */
+static struct platform_driver mxc_w1_driver = {
+ .driver = {
+ .name = "mxc_w1",
+ },
+ .probe = mxc_w1_probe,
+ .remove = mxc_w1_remove,
+};
+
+/*!
+ * This structure is used to store
+ * information specific to w1 module.
+ */
+
+struct mxc_w1_device {
+ char *base_address;
+ unsigned long found;
+ unsigned int clkdiv;
+ struct clk *clk;
+ struct w1_bus_master *bus_master;
+};
+
+/*
+ * this is the low level routine to
+ * reset the device on the One Wire interface
+ * on the hardware
+ * @param data the data field of the w1 device structure
+ * @return the function returns 0 when the reset pulse has
+ * been generated
+ */
+static u8 mxc_w1_ds2_reset_bus(void *data)
+{
+ volatile u8 reg_val;
+ u8 ret;
+ struct mxc_w1_device *dev = (struct mxc_w1_device *)data;
+
+ __raw_writeb(0x80, (dev->base_address + MXC_W1_CONTROL));
+
+ do {
+ reg_val = __raw_readb(dev->base_address + MXC_W1_CONTROL);
+ } while (((reg_val >> 7) & 0x1) != 0);
+ ret = ((reg_val >> 7) & 0x1);
+ return ret;
+}
+
+/*!
+ * this is the low level routine to read/write a bit on the One Wire
+ * interface on the hardware
+ * @param data the data field of the w1 device structure
+ * @param bit 0 = write-0 cycle, 1 = write-1/read cycle
+ * @return the function returns the bit read (0 or 1)
+ */
+static u8 mxc_w1_ds2_touch_bit(void *data, u8 bit)
+{
+
+ volatile u8 reg_val;
+ struct mxc_w1_device *dev = (struct mxc_w1_device *)data;
+ u8 ret = 0;
+
+ if (0 == bit) {
+ __raw_writeb((1 << 5), (dev->base_address + MXC_W1_CONTROL));
+
+ do {
+ reg_val =
+ __raw_readb(dev->base_address + MXC_W1_CONTROL);
+ } while (0 != ((reg_val >> 5) & 0x1));
+ }
+
+ else {
+ __raw_writeb((1 << 4), dev->base_address + MXC_W1_CONTROL);
+ do {
+ reg_val =
+ __raw_readb(dev->base_address + MXC_W1_CONTROL);
+ } while (0 != ((reg_val >> 4) & 0x1));
+
+ reg_val =
+ (((__raw_readb(dev->base_address + MXC_W1_CONTROL)) >> 3) &
+ 0x1);
+ ret = (u8) (reg_val);
+ }
+
+ return ret;
+}
+
+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->base_address + MXC_W1_TXRX));
+ __raw_writeb(0x10, (dev->base_address + 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->base_address + 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->base_address + 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->base_address + MXC_W1_INTERRUPT));
+ if ((reg_val & 0x10)) {
+ complete(&transmit_done);
+ reg_val = __raw_readb((dev->base_address + 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->base_address + MXC_W1_CONTROL));
+ mdelay(1);
+ mxc_w1_ds2_write_byte(data, 0xF0);
+ __raw_writeb(0x02, (dev->base_address + 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);
+ }
+}
+
+/*!
+ * this routine sets the One Wire clock
+ * to a value of 1 Mhz, as required by
+ * hardware.
+ * @param dev the device structure for w1
+ * @return The function returns void
+ */
+static void mxc_w1_hw_init(struct mxc_w1_device *dev)
+{
+ clk_enable(dev->clk);
+
+ /* set the timer divider clock to divide by 65 */
+ /* as the clock to the One Wire is at 66.5MHz */
+ __raw_writeb(dev->clkdiv, dev->base_address + MXC_W1_TIME_DIVIDER);
+
+ return;
+}
+
+/*!
+ * this is the probe routine for the One Wire driver.
+ * It is called during the driver initilaization.
+ * @param pdev the platform device structure for w1
+ * @return The function returns 0 on success
+ * and a non-zero value on failure
+ *
+ */
+static int __devinit mxc_w1_probe(struct platform_device *pdev)
+{
+ struct mxc_w1_device *dev;
+ struct mxc_w1_config *data =
+ (struct mxc_w1_config *)pdev->dev.platform_data;
+ int flag, ret_val, irq = -ENXIO;
+ int err = 0;
+ ret_val = 0;
+ flag = data->search_rom_accelerator;
+ dev = kzalloc(sizeof(struct mxc_w1_device) +
+ sizeof(struct w1_bus_master), GFP_KERNEL);
+ if (!dev) {
+ return -ENOMEM;
+ }
+ dev->clk = clk_get(&pdev->dev, "owire_clk");
+ dev->bus_master = (struct w1_bus_master *)(dev + 1);
+ dev->found = 1;
+ dev->clkdiv = (clk_get_rate(dev->clk) / 1000000) - 1;
+ dev->base_address = (void *)IO_ADDRESS(OWIRE_BASE_ADDR);
+
+ mxc_w1_hw_init(dev);
+ dev->bus_master->data = dev;
+ dev->bus_master->reset_bus = &mxc_w1_ds2_reset_bus;
+ dev->bus_master->touch_bit = &mxc_w1_ds2_touch_bit;
+ if (flag) {
+ dev->bus_master->write_byte = &mxc_w1_ds2_write_byte;
+ dev->bus_master->read_byte = &mxc_w1_ds2_read_byte;
+ dev->bus_master->search = &search_ROM_accelerator;
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ err = -ENOENT;
+ goto err_out_free_device;
+ }
+ ret_val =
+ request_irq(irq, w1_interrupt_handler, 0, "mxc_w1", dev);
+ if (ret_val) {
+ pr_debug("OWire:request_irq(%d) returned error %d\n",
+ irq, ret_val);
+ err = -1;
+ goto err_out_free_device;
+ }
+ }
+ err = w1_add_master_device(dev->bus_master);
+ if (err)
+ goto err_irq;
+
+ platform_set_drvdata(pdev, dev);
+ return 0;
+
+err_irq:
+ if (irq >= 0)
+ free_irq(irq, dev);
+err_out_free_device:
+ kfree(dev);
+ return err;
+}
+
+/*
+ * disassociate the w1 device from the driver
+ * @param dev the device structure for w1
+ * @return The function returns void
+ */
+static int mxc_w1_remove(struct platform_device *pdev)
+{
+ struct mxc_w1_device *dev = platform_get_drvdata(pdev);
+ struct mxc_w1_config *data =
+ (struct mxc_w1_config *)pdev->dev.platform_data;
+ int irq;
+
+ if (dev->found) {
+ w1_remove_master_device(dev->bus_master);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if ((irq >= 0) && (data->search_rom_accelerator))
+ free_irq(irq, dev);
+
+ clk_disable(dev->clk);
+ clk_put(dev->clk);
+
+ kfree(dev);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+/*
+ * initialize the driver
+ * @return The function returns 0 on success
+ * and a non-zero value on failure
+ */
+
+static int __init mxc_w1_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO "Serial: MXC OWire driver\n");
+
+ gpio_owire_active();
+
+ ret = platform_driver_register(&mxc_w1_driver);
+
+ return ret;
+}
+
+/*
+ * cleanup before the driver exits
+ */
+static void mxc_w1_exit(void)
+{
+ gpio_owire_inactive();
+ platform_driver_unregister(&mxc_w1_driver);
+}
+
+module_init(mxc_w1_init);
+module_exit(mxc_w1_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Freescale Semiconductors Inc");
+MODULE_DESCRIPTION("Driver for One-Wire on MXC");
diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig
index 8d0b1fb1e52e..ad77126801f5 100644
--- a/drivers/w1/slaves/Kconfig
+++ b/drivers/w1/slaves/Kconfig
@@ -16,12 +16,40 @@ 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_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_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)"
+ 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 990f400b6d22..fd53f53d8681 100644
--- a/drivers/w1/slaves/Makefile
+++ b/drivers/w1/slaves/Makefile
@@ -4,6 +4,9 @@
obj-$(CONFIG_W1_SLAVE_THERM) += w1_therm.o
obj-$(CONFIG_W1_SLAVE_SMEM) += w1_smem.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_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..a3434ad5994c
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2438.c
@@ -0,0 +1,585 @@
+/*
+ * Copyright 2008 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 <asm/types.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/idr.h>
+#include <linux/power_supply.h>
+
+#include "../w1.h"
+#include "../w1_int.h"
+#include "../w1_family.h"
+#include "w1_ds2438.h"
+
+struct ds2438_device_info {
+ /* DS2438 data, valid after calling ds2438_battery_read_status() */
+ unsigned long update_time; /* jiffies when data read */
+ char raw[DS2438_PAGE_SIZE]; /* raw DS2438 data */
+ int voltage_uV;
+ int current_uA;
+ int accum_current_uAh;
+ int temp_C;
+ int charge_status;
+ u8 init:1;
+ u8 setup:1;
+ u8 calibrate:1;
+ u8 input_src:1;
+ u8 ee_flg:1;
+ u8 resv_bit:3;
+ u8 threshold:8;
+ u16 resv_bytes;
+ u32 senser;
+
+ struct power_supply bat;
+ struct device *w1_dev;
+ struct ds2438_ops ops;
+ struct workqueue_struct *monitor_wqueue;
+ struct delayed_work monitor_work;
+};
+
+#define DS2438_SENSER 25
+#define to_ds2438_device_info(x) container_of((x), struct ds2438_device_info, \
+ bat);
+
+
+static enum power_supply_property ds2438_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+};
+
+static char ds2438_sensers_title[] = "DS2438 senserin thousands of resister:";
+static unsigned int cache_time = 1000;
+module_param(cache_time, uint, 0644);
+MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
+
+static ssize_t ds2438_show_input(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct ds2438_device_info *di = to_ds2438_device_info(psy);
+
+ return sprintf(buf, "%s\n", di->input_src ? "1:VDD" : "0:VAD");
+}
+
+static ssize_t ds2438_show_senser(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int len;
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct ds2438_device_info *di = to_ds2438_device_info(psy);
+
+ len = sprintf(buf, "%s\n", ds2438_sensers_title);
+ len += sprintf(buf + len, "%d\n", di->senser);
+ return len;
+}
+
+static ssize_t ds2438_show_ee(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct ds2438_device_info *di = to_ds2438_device_info(psy);
+
+ return sprintf(buf, "%d\n", di->ee_flg);
+}
+
+static ssize_t ds2438_show_threshold(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct ds2438_device_info *di = to_ds2438_device_info(psy);
+
+ return sprintf(buf, "%d\n", di->threshold);
+}
+
+static ssize_t ds2438_set_input(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct ds2438_device_info *di = to_ds2438_device_info(psy);
+ di->input_src = !!simple_strtoul(buf, NULL, 0);
+ return count;
+}
+
+static ssize_t ds2438_set_senser(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ u32 resister;
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct ds2438_device_info *di = to_ds2438_device_info(psy);
+ resister = simple_strtoul(buf, NULL, 0);
+ if (resister)
+ di->senser = resister;
+ return count;
+}
+
+static ssize_t ds2438_set_ee(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct ds2438_device_info *di = to_ds2438_device_info(psy);
+
+ di->ee_flg = !!simple_strtoul(buf, NULL, 0);
+ di->setup = 1;
+ return count;
+}
+
+static ssize_t ds2438_set_threshold(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int threshold;
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct ds2438_device_info *di = to_ds2438_device_info(psy);
+
+ threshold = simple_strtoul(buf, NULL, 0);
+ if (threshold < 256) {
+ di->threshold = threshold;
+ di->setup = 1;
+ return count;
+ }
+ return -EINVAL;
+}
+
+static ssize_t ds2438_set_calibrate(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct ds2438_device_info *di = to_ds2438_device_info(psy);
+
+ di->calibrate = !!simple_strtoul(buf, NULL, 0);
+ return count;
+}
+
+static struct device_attribute ds2438_dev_attr[] = {
+ __ATTR(input_src, 0664, ds2438_show_input, ds2438_set_input),
+ __ATTR(senser, 0664, ds2438_show_senser, ds2438_set_senser),
+ __ATTR(ee_flg, 0664, ds2438_show_ee, ds2438_set_ee),
+ __ATTR(threshold, 0664, ds2438_show_threshold, ds2438_set_threshold),
+ __ATTR(calibrate, 0220, NULL, ds2438_set_calibrate),
+};
+
+static void ds2438_setup(struct ds2438_device_info *di)
+{
+ di->ops.load_sram(di->w1_dev, PAGE0_CONTROL);
+ di->ops.read_page(di->w1_dev, PAGE0_CONTROL, di->raw);
+ if (di->init && di->setup) {
+ if (di->ee_flg)
+ di->raw[PAGE0_STAT_CTRL] |= DS2438_CTRL_EE;
+ else
+ di->raw[PAGE0_STAT_CTRL] &= ~DS2438_CTRL_EE;
+ if (di->input_src)
+ di->raw[PAGE0_STAT_CTRL] |= DS2438_CTRL_AD;
+ else
+ di->raw[PAGE0_STAT_CTRL] &= ~DS2438_CTRL_AD;
+ di->raw[PAGE0_THRESHOLD] = di->threshold;
+ } else {
+ di->ee_flg = !!(di->raw[PAGE0_STAT_CTRL] & DS2438_CTRL_EE);
+ di->input_src = !!(di->raw[PAGE0_STAT_CTRL] & DS2438_CTRL_AD);
+ di->threshold = di->raw[PAGE0_THRESHOLD];
+ di->raw[PAGE0_STAT_CTRL] |= DS2438_CTRL_IAD | DS2438_CTRL_CA;
+ }
+ di->ops.write_page(di->w1_dev, PAGE0_CONTROL, di->raw);
+ di->ops.drain_sram(di->w1_dev, PAGE0_CONTROL);
+ if (!di->init) {
+ di->calibrate = 1;
+ di->init = 1;
+ }
+ di->setup = 0;
+}
+
+static void ds2438_calibrate(struct ds2438_device_info *di)
+{
+ int current_raw;
+ /* disable ICA */
+ di->ops.load_sram(di->w1_dev, PAGE0_CONTROL);
+ di->ops.read_page(di->w1_dev, PAGE0_CONTROL, di->raw);
+ di->raw[PAGE0_STAT_CTRL] &= ~DS2438_CTRL_IAD;
+ di->ops.write_page(di->w1_dev, PAGE0_CONTROL, di->raw);
+ di->ops.drain_sram(di->w1_dev, PAGE0_CONTROL);
+
+ /* Zero offset */
+ di->ops.load_sram(di->w1_dev, PAGE1_ETM);
+ di->ops.read_page(di->w1_dev, PAGE1_ETM, di->raw);
+ ds2438_writew(di->raw + PAGE1_OFFSET_LSB, 0);
+ di->ops.drain_sram(di->w1_dev, PAGE1_ETM_BYTE0);
+
+ /* enable ICA & read current */
+ di->ops.load_sram(di->w1_dev, PAGE0_CONTROL);
+ di->ops.read_page(di->w1_dev, PAGE0_CONTROL, di->raw);
+ di->raw[PAGE0_STAT_CTRL] |= DS2438_CTRL_IAD;
+ di->ops.write_page(di->w1_dev, PAGE0_CONTROL, di->raw);
+ di->ops.drain_sram(di->w1_dev, PAGE0_CONTROL);
+ /*wait current convert about 36HZ */
+ mdelay(30);
+ /* disable ICA */
+ di->ops.load_sram(di->w1_dev, PAGE0_CONTROL);
+ di->ops.read_page(di->w1_dev, PAGE0_CONTROL, di->raw);
+ di->raw[PAGE0_STAT_CTRL] &= ~DS2438_CTRL_IAD;
+ di->ops.write_page(di->w1_dev, PAGE0_CONTROL, di->raw);
+ di->ops.drain_sram(di->w1_dev, PAGE0_CONTROL);
+ /* read current value */
+ current_raw = ds2438_readw(di->raw + PAGE0_CURRENT_LSB);
+ /* write offset by current value */
+ di->ops.load_sram(di->w1_dev, PAGE1_ETM);
+ di->ops.read_page(di->w1_dev, PAGE1_ETM, di->raw);
+ ds2438_writew(di->raw + PAGE1_OFFSET_LSB, current_raw << 8);
+ di->ops.write_page(di->w1_dev, PAGE1_ETM, di->raw);
+ di->ops.drain_sram(di->w1_dev, PAGE1_ETM);
+
+ /*enable ICA again */
+ di->ops.load_sram(di->w1_dev, PAGE0_CONTROL);
+ di->ops.read_page(di->w1_dev, PAGE0_CONTROL, di->raw);
+ di->raw[PAGE0_STAT_CTRL] |= DS2438_CTRL_IAD;
+ di->ops.write_page(di->w1_dev, PAGE0_CONTROL, di->raw);
+ di->ops.drain_sram(di->w1_dev, PAGE0_CONTROL);
+ di->calibrate = 0;
+}
+
+/*
+ * power supply temperture is in tenths of degree.
+ */
+static inline int ds2438_get_temp(u16 raw)
+{
+ int degree, s;
+ s = !!(raw & 0x8000);
+
+ if (s)
+ raw = ((~raw & 0x7FFF) + 1);
+ degree = ((raw >> 8) * 10) + (((raw & 0xFF) * 5) + 63) / 128;
+ return s ? -degree : degree;
+}
+
+/*
+ * power supply current is in uA.
+ */
+static inline int ds2438_get_current(u32 senser, u16 raw)
+{
+ int s, current_uA;
+ s = !!(raw & 0xFC00);
+ /* (x * 1000 * 1000)uA / (4096 * (Rsens / 1000)) */
+ raw &= 0x3FF;
+ current_uA = raw * 125 * 125 * 125;
+ current_uA /= (senser << 3);
+ return s ? -current_uA : current_uA;
+}
+
+/*
+ * power supply current is in uAh.
+ */
+static inline int ds2438_get_ica(u32 senser, u8 raw)
+{
+ int charge_uAh;
+ /* (x * 1000 * 1000)uA / (2048 * (Rsens / 1000)) */
+ charge_uAh = (raw * 125 * 125 * 125) >> 4;
+ charge_uAh /= (senser << 4);
+ return charge_uAh;
+}
+
+static int ds2438_battery_update_page1(struct ds2438_device_info *di)
+{
+ int ica_raw;
+ di->ops.load_sram(di->w1_dev, PAGE1_ETM);
+ di->ops.read_page(di->w1_dev, PAGE1_ETM, di->raw);
+ ica_raw = di->raw[PAGE1_ICA];
+ di->accum_current_uAh = ds2438_get_ica(di->senser, ica_raw);
+ return 0;
+}
+
+static int ds2438_battery_read_status(struct ds2438_device_info *di)
+{
+ u8 status;
+ int temp_raw, voltage_raw, current_raw;
+
+ if (!(di->init) || di->setup)
+ ds2438_setup(di);
+
+ if (di->calibrate)
+ ds2438_calibrate(di);
+
+ if (di->update_time && time_before(jiffies, di->update_time +
+ msecs_to_jiffies(cache_time)))
+ return 0;
+
+ di->ops.load_sram(di->w1_dev, PAGE0_CONTROL);
+ di->ops.read_page(di->w1_dev, PAGE0_CONTROL, di->raw);
+ status = di->raw[PAGE0_STAT_CTRL];
+ temp_raw = ds2438_readw(di->raw + PAGE0_TEMP_LSB);
+ voltage_raw = ds2438_readw(di->raw + PAGE0_VOLTAGE_LSB);
+ current_raw = ds2438_readw(di->raw + PAGE0_CURRENT_LSB);
+ di->temp_C = ds2438_get_temp(temp_raw);
+ di->voltage_uV = voltage_raw * 10000;
+ di->current_uA = ds2438_get_current(di->senser, current_raw);
+
+ ds2438_battery_update_page1(di);
+
+ if (!(status & DS2438_STAT_TB))
+ di->ops.command(di->w1_dev, DS2438_CONVERT_TEMP, 0);
+ if (!(status & DS2438_STAT_ADB))
+ di->ops.command(di->w1_dev, DS2438_CONVERT_VOLT, 0);
+ di->update_time = jiffies;
+ return 0;
+}
+
+static void ds2438_battery_update_status(struct ds2438_device_info *di)
+{
+ int old_charge_status = di->charge_status;
+
+ ds2438_battery_read_status(di);
+
+ if (di->charge_status != old_charge_status)
+ power_supply_changed(&di->bat);
+}
+
+static void ds2438_battery_work(struct work_struct *work)
+{
+ struct ds2438_device_info *di = container_of(work,
+ struct ds2438_device_info,
+ monitor_work.work);
+ const int interval = HZ * 60;
+
+ dev_dbg(di->w1_dev, "%s\n", __func__);
+
+ ds2438_battery_update_status(di);
+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval);
+}
+
+static void ds2438_battery_external_power_changed(struct power_supply *psy)
+{
+ struct ds2438_device_info *di = to_ds2438_device_info(psy);
+
+ dev_dbg(di->w1_dev, "%s\n", __func__);
+
+ cancel_delayed_work(&di->monitor_work);
+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ / 10);
+}
+
+static int ds2438_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct ds2438_device_info *di = to_ds2438_device_info(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = di->charge_status;
+ return 0;
+ default:
+ break;
+ }
+
+ ds2438_battery_read_status(di);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = di->voltage_uV;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = di->current_uA;
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = di->temp_C;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = di->accum_current_uAh;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* W1 slave DS2438 famliy operations */
+static int 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;
+}
+
+static int 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;
+}
+
+static int 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;
+}
+
+static int ds2438_drain_sram(struct device *dev, u8 page)
+{
+ return ds2438_command(dev, DS2438_COPY_SCRATCHPAD, page);
+}
+
+static int ds2438_load_sram(struct device *dev, u8 page)
+{
+ return ds2438_command(dev, DS2438_RECALL_MEMORY, page);
+}
+
+static inline void ds2438_defaut_ops(struct ds2438_ops *ops)
+{
+ ops->read_page = ds2438_read_page;
+ ops->write_page = ds2438_write_page;
+ ops->drain_sram = ds2438_drain_sram;
+ ops->load_sram = ds2438_load_sram;
+ ops->command = ds2438_command;
+}
+
+static int ds2438_add_slave(struct w1_slave *slave)
+{
+ int i, retval = 0;
+ struct ds2438_device_info *di;
+
+ di = kzalloc(sizeof(*di), GFP_KERNEL);
+ if (!di) {
+ retval = -ENOMEM;
+ goto di_alloc_failed;
+ }
+
+ di->w1_dev = &slave->dev;
+ di->bat.name = slave->dev.bus_id;
+ di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
+ di->bat.properties = ds2438_battery_props;
+ di->bat.num_properties = ARRAY_SIZE(ds2438_battery_props);
+ di->bat.get_property = ds2438_battery_get_property;
+ di->bat.external_power_changed = ds2438_battery_external_power_changed;
+ ds2438_defaut_ops(&di->ops);
+ di->senser = DS2438_SENSER;
+ di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
+
+ retval = power_supply_register(&slave->dev, &di->bat);
+ if (retval) {
+ dev_err(&slave->dev, "failed to register battery\n");
+ goto batt_failed;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ds2438_dev_attr); i++) {
+ if (device_create_file(di->bat.dev, ds2438_dev_attr + i)) {
+ printk(KERN_ERR "Customize attribute file fail!\n");
+ break;
+ }
+ }
+
+ if (i != ARRAY_SIZE(ds2438_dev_attr)) {
+ for (; i >= 0; i++)
+ device_remove_file(di->bat.dev, ds2438_dev_attr + i);
+ goto workqueue_failed;
+ }
+ INIT_DELAYED_WORK(&di->monitor_work, ds2438_battery_work);
+ di->monitor_wqueue = create_singlethread_workqueue(slave->dev.bus_id);
+ if (!di->monitor_wqueue) {
+ retval = -ESRCH;
+ goto workqueue_failed;
+ }
+ dev_set_drvdata(&slave->dev, di);
+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ / 2);
+
+ goto success;
+
+ workqueue_failed:
+ power_supply_unregister(&di->bat);
+ batt_failed:
+ kfree(di);
+ di_alloc_failed:
+ success:
+ return retval;
+}
+
+static void ds2438_remove_slave(struct w1_slave *slave)
+{
+ struct ds2438_device_info *di = dev_get_drvdata(&slave->dev);
+
+ cancel_rearming_delayed_workqueue(di->monitor_wqueue,
+ &di->monitor_work);
+ destroy_workqueue(di->monitor_wqueue);
+ power_supply_unregister(&di->bat);
+}
+
+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");
+ return w1_register_family(&w1_family_ds2438);
+}
+
+static void __exit w1_ds2438_fini(void)
+{
+ w1_unregister_family(&w1_family_ds2438);
+}
+
+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..fe22b6ec253b
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2438.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2008 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;
+}
+#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..9346a21bdc70
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2751.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2005-2007 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/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
+
+ if ((count = w1_f51_fix_count(off, count, W1_EEPROM_SIZE)) == 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;
+
+ if ((count = w1_f51_fix_count(off, count, W1_EEPROM_SIZE)) == 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