summaryrefslogtreecommitdiffstats
path: root/drivers/w1
diff options
context:
space:
mode:
authorTimothy Pearson <tpearson@raptorengineering.com>2017-08-23 14:45:25 -0500
committerTimothy Pearson <tpearson@raptorengineering.com>2017-08-23 14:45:25 -0500
commitfcbb27b0ec6dcbc5a5108cb8fb19eae64593d204 (patch)
tree22962a4387943edc841c72a4e636a068c66d58fd /drivers/w1
downloadast2050-linux-kernel-fcbb27b0ec6dcbc5a5108cb8fb19eae64593d204.zip
ast2050-linux-kernel-fcbb27b0ec6dcbc5a5108cb8fb19eae64593d204.tar.gz
Initial import of modified Linux 2.6.28 tree
Original upstream URL: git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git | branch linux-2.6.28.y
Diffstat (limited to 'drivers/w1')
-rw-r--r--drivers/w1/Kconfig31
-rw-r--r--drivers/w1/Makefile9
-rw-r--r--drivers/w1/masters/Kconfig63
-rw-r--r--drivers/w1/masters/Makefile10
-rw-r--r--drivers/w1/masters/ds1wm.c472
-rw-r--r--drivers/w1/masters/ds2482.c548
-rw-r--r--drivers/w1/masters/ds2490.c1027
-rw-r--r--drivers/w1/masters/matrox_w1.c247
-rw-r--r--drivers/w1/masters/omap_hdq.c725
-rw-r--r--drivers/w1/masters/w1-gpio.c124
-rw-r--r--drivers/w1/slaves/Kconfig54
-rw-r--r--drivers/w1/slaves/Makefile9
-rw-r--r--drivers/w1/slaves/w1_bq27000.c123
-rw-r--r--drivers/w1/slaves/w1_ds2431.c312
-rw-r--r--drivers/w1/slaves/w1_ds2433.c316
-rw-r--r--drivers/w1/slaves/w1_ds2760.c209
-rw-r--r--drivers/w1/slaves/w1_ds2760.h50
-rw-r--r--drivers/w1/slaves/w1_smem.c70
-rw-r--r--drivers/w1/slaves/w1_therm.c255
-rw-r--r--drivers/w1/w1.c1054
-rw-r--r--drivers/w1/w1.h243
-rw-r--r--drivers/w1/w1_family.c139
-rw-r--r--drivers/w1/w1_family.h67
-rw-r--r--drivers/w1/w1_int.c250
-rw-r--r--drivers/w1/w1_int.h34
-rw-r--r--drivers/w1/w1_io.c383
-rw-r--r--drivers/w1/w1_log.h38
-rw-r--r--drivers/w1/w1_netlink.c245
-rw-r--r--drivers/w1/w1_netlink.h74
29 files changed, 7181 insertions, 0 deletions
diff --git a/drivers/w1/Kconfig b/drivers/w1/Kconfig
new file mode 100644
index 0000000..9adbb4f
--- /dev/null
+++ b/drivers/w1/Kconfig
@@ -0,0 +1,31 @@
+menuconfig W1
+ tristate "Dallas's 1-wire support"
+ depends on HAS_IOMEM
+ ---help---
+ Dallas' 1-wire bus is useful to connect slow 1-pin devices
+ such as iButtons and thermal sensors.
+
+ If you want W1 support, you should say Y here.
+
+ This W1 support can also be built as a module. If so, the module
+ will be called wire.ko.
+
+if W1
+
+config W1_CON
+ depends on CONNECTOR
+ bool "Userspace communication over connector"
+ default y
+ --- help ---
+ This allows to communicate with userspace using connector. For more
+ information see <file:Documentation/connector/connector.txt>.
+ There are three types of messages between w1 core and userspace:
+ 1. Events. They are generated each time new master or slave device found
+ either due to automatic or requested search.
+ 2. Userspace commands. Includes read/write and search/alarm search commands.
+ 3. Replies to userspace commands.
+
+source drivers/w1/masters/Kconfig
+source drivers/w1/slaves/Kconfig
+
+endif # W1
diff --git a/drivers/w1/Makefile b/drivers/w1/Makefile
new file mode 100644
index 0000000..6bb0b54
--- /dev/null
+++ b/drivers/w1/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the Dallas's 1-wire bus.
+#
+
+obj-$(CONFIG_W1) += wire.o
+wire-objs := w1.o w1_int.o w1_family.o w1_netlink.o w1_io.o
+
+obj-y += masters/ slaves/
+
diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig
new file mode 100644
index 0000000..9061682
--- /dev/null
+++ b/drivers/w1/masters/Kconfig
@@ -0,0 +1,63 @@
+#
+# 1-wire bus master configuration
+#
+
+menu "1-wire Bus Masters"
+
+config W1_MASTER_MATROX
+ tristate "Matrox G400 transport layer for 1-wire"
+ depends on PCI
+ help
+ Say Y here if you want to communicate with your 1-wire devices
+ using Matrox's G400 GPIO pins.
+
+ This support is also available as a module. If so, the module
+ will be called matrox_w1.ko.
+
+config W1_MASTER_DS2490
+ tristate "DS2490 USB <-> W1 transport layer for 1-wire"
+ depends on USB
+ help
+ Say Y here if you want to have a driver for DS2490 based USB <-> W1 bridges,
+ for example DS9490*.
+
+ This support is also available as a module. If so, the module
+ will be called ds2490.ko.
+
+config W1_MASTER_DS2482
+ tristate "Maxim DS2482 I2C to 1-Wire bridge"
+ depends on I2C && EXPERIMENTAL
+ help
+ If you say yes here you get support for the Maxim DS2482
+ I2C to 1-Wire bridge.
+
+ This driver can also be built as a module. If so, the module
+ will be called ds2482.
+
+config W1_MASTER_DS1WM
+ tristate "Maxim DS1WM 1-wire busmaster"
+ depends on W1 && ARM && HAVE_CLK
+ help
+ Say Y here to enable the DS1WM 1-wire driver, such as that
+ in HP iPAQ devices like h5xxx, h2200, and ASIC3-based like
+ hx4700.
+
+config W1_MASTER_GPIO
+ tristate "GPIO 1-wire busmaster"
+ depends on GENERIC_GPIO
+ help
+ Say Y here if you want to communicate with your 1-wire devices using
+ GPIO pins. This driver uses the GPIO API to control the wire.
+
+ This support is also available as a module. If so, the module
+ will be called w1-gpio.ko.
+
+config HDQ_MASTER_OMAP
+ tristate "OMAP HDQ driver"
+ depends on ARCH_OMAP2430 || ARCH_OMAP34XX
+ help
+ Say Y here if you want support for the 1-wire or HDQ Interface
+ on an OMAP processor.
+
+endmenu
+
diff --git a/drivers/w1/masters/Makefile b/drivers/w1/masters/Makefile
new file mode 100644
index 0000000..bc4714a
--- /dev/null
+++ b/drivers/w1/masters/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for 1-wire bus master drivers.
+#
+
+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_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/ds1wm.c b/drivers/w1/masters/ds1wm.c
new file mode 100644
index 0000000..29e144f
--- /dev/null
+++ b/drivers/w1/masters/ds1wm.c
@@ -0,0 +1,472 @@
+/*
+ * 1-wire busmaster driver for DS1WM and ASICs with embedded DS1WMs
+ * such as HP iPAQs (including h5xxx, h2200, and devices with ASIC3
+ * like hx4700).
+ *
+ * Copyright (c) 2004-2005, Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>
+ * Copyright (c) 2004-2007, Matt Reimer <mreimer@vpop.net>
+ *
+ * Use consistent with the GNU GPL is permitted,
+ * provided that this copyright notice is
+ * preserved in its entirety in all copies and derived works.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/ds1wm.h>
+
+#include <asm/io.h>
+
+#include "../w1.h"
+#include "../w1_int.h"
+
+
+#define DS1WM_CMD 0x00 /* R/W 4 bits command */
+#define DS1WM_DATA 0x01 /* R/W 8 bits, transmit/receive buffer */
+#define DS1WM_INT 0x02 /* R/W interrupt status */
+#define DS1WM_INT_EN 0x03 /* R/W interrupt enable */
+#define DS1WM_CLKDIV 0x04 /* R/W 5 bits of divisor and pre-scale */
+
+#define DS1WM_CMD_1W_RESET (1 << 0) /* force reset on 1-wire bus */
+#define DS1WM_CMD_SRA (1 << 1) /* enable Search ROM accelerator mode */
+#define DS1WM_CMD_DQ_OUTPUT (1 << 2) /* write only - forces bus low */
+#define DS1WM_CMD_DQ_INPUT (1 << 3) /* read only - reflects state of bus */
+#define DS1WM_CMD_RST (1 << 5) /* software reset */
+#define DS1WM_CMD_OD (1 << 7) /* overdrive */
+
+#define DS1WM_INT_PD (1 << 0) /* presence detect */
+#define DS1WM_INT_PDR (1 << 1) /* presence detect result */
+#define DS1WM_INT_TBE (1 << 2) /* tx buffer empty */
+#define DS1WM_INT_TSRE (1 << 3) /* tx shift register empty */
+#define DS1WM_INT_RBF (1 << 4) /* rx buffer full */
+#define DS1WM_INT_RSRF (1 << 5) /* rx shift register full */
+
+#define DS1WM_INTEN_EPD (1 << 0) /* enable presence detect int */
+#define DS1WM_INTEN_IAS (1 << 1) /* INTR active state */
+#define DS1WM_INTEN_ETBE (1 << 2) /* enable tx buffer empty int */
+#define DS1WM_INTEN_ETMT (1 << 3) /* enable tx shift register empty int */
+#define DS1WM_INTEN_ERBF (1 << 4) /* enable rx buffer full int */
+#define DS1WM_INTEN_ERSRF (1 << 5) /* enable rx shift register full int */
+#define DS1WM_INTEN_DQO (1 << 6) /* enable direct bus driving ops */
+
+
+#define DS1WM_TIMEOUT (HZ * 5)
+
+static struct {
+ unsigned long freq;
+ unsigned long divisor;
+} freq[] = {
+ { 4000000, 0x8 },
+ { 5000000, 0x2 },
+ { 6000000, 0x5 },
+ { 7000000, 0x3 },
+ { 8000000, 0xc },
+ { 10000000, 0x6 },
+ { 12000000, 0x9 },
+ { 14000000, 0x7 },
+ { 16000000, 0x10 },
+ { 20000000, 0xa },
+ { 24000000, 0xd },
+ { 28000000, 0xb },
+ { 32000000, 0x14 },
+ { 40000000, 0xe },
+ { 48000000, 0x11 },
+ { 56000000, 0xf },
+ { 64000000, 0x18 },
+ { 80000000, 0x12 },
+ { 96000000, 0x15 },
+ { 112000000, 0x13 },
+ { 128000000, 0x1c },
+};
+
+struct ds1wm_data {
+ void __iomem *map;
+ int bus_shift; /* # of shifts to calc register offsets */
+ struct platform_device *pdev;
+ struct ds1wm_platform_data *pdata;
+ int irq;
+ int active_high;
+ struct clk *clk;
+ int slave_present;
+ void *reset_complete;
+ void *read_complete;
+ void *write_complete;
+ u8 read_byte; /* last byte received */
+};
+
+static inline void ds1wm_write_register(struct ds1wm_data *ds1wm_data, u32 reg,
+ u8 val)
+{
+ __raw_writeb(val, ds1wm_data->map + (reg << ds1wm_data->bus_shift));
+}
+
+static inline u8 ds1wm_read_register(struct ds1wm_data *ds1wm_data, u32 reg)
+{
+ return __raw_readb(ds1wm_data->map + (reg << ds1wm_data->bus_shift));
+}
+
+
+static irqreturn_t ds1wm_isr(int isr, void *data)
+{
+ struct ds1wm_data *ds1wm_data = data;
+ u8 intr = ds1wm_read_register(ds1wm_data, DS1WM_INT);
+
+ ds1wm_data->slave_present = (intr & DS1WM_INT_PDR) ? 0 : 1;
+
+ if ((intr & DS1WM_INT_PD) && ds1wm_data->reset_complete)
+ complete(ds1wm_data->reset_complete);
+
+ if ((intr & DS1WM_INT_TSRE) && ds1wm_data->write_complete)
+ complete(ds1wm_data->write_complete);
+
+ if (intr & DS1WM_INT_RBF) {
+ ds1wm_data->read_byte = ds1wm_read_register(ds1wm_data,
+ DS1WM_DATA);
+ if (ds1wm_data->read_complete)
+ complete(ds1wm_data->read_complete);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int ds1wm_reset(struct ds1wm_data *ds1wm_data)
+{
+ unsigned long timeleft;
+ DECLARE_COMPLETION_ONSTACK(reset_done);
+
+ ds1wm_data->reset_complete = &reset_done;
+
+ ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, DS1WM_INTEN_EPD |
+ (ds1wm_data->active_high ? DS1WM_INTEN_IAS : 0));
+
+ ds1wm_write_register(ds1wm_data, DS1WM_CMD, DS1WM_CMD_1W_RESET);
+
+ timeleft = wait_for_completion_timeout(&reset_done, DS1WM_TIMEOUT);
+ ds1wm_data->reset_complete = NULL;
+ if (!timeleft) {
+ dev_err(&ds1wm_data->pdev->dev, "reset failed\n");
+ return 1;
+ }
+
+ /* Wait for the end of the reset. According to the specs, the time
+ * from when the interrupt is asserted to the end of the reset is:
+ * tRSTH - tPDH - tPDL - tPDI
+ * 625 us - 60 us - 240 us - 100 ns = 324.9 us
+ *
+ * We'll wait a bit longer just to be sure.
+ * Was udelay(500), but if it is going to busywait the cpu that long,
+ * might as well come back later.
+ */
+ msleep(1);
+
+ ds1wm_write_register(ds1wm_data, DS1WM_INT_EN,
+ DS1WM_INTEN_ERBF | DS1WM_INTEN_ETMT | DS1WM_INTEN_EPD |
+ (ds1wm_data->active_high ? DS1WM_INTEN_IAS : 0));
+
+ if (!ds1wm_data->slave_present) {
+ dev_dbg(&ds1wm_data->pdev->dev, "reset: no devices found\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+static int ds1wm_write(struct ds1wm_data *ds1wm_data, u8 data)
+{
+ DECLARE_COMPLETION_ONSTACK(write_done);
+ ds1wm_data->write_complete = &write_done;
+
+ ds1wm_write_register(ds1wm_data, DS1WM_DATA, data);
+
+ wait_for_completion_timeout(&write_done, DS1WM_TIMEOUT);
+ ds1wm_data->write_complete = NULL;
+
+ return 0;
+}
+
+static int ds1wm_read(struct ds1wm_data *ds1wm_data, unsigned char write_data)
+{
+ DECLARE_COMPLETION_ONSTACK(read_done);
+ ds1wm_data->read_complete = &read_done;
+
+ ds1wm_write(ds1wm_data, write_data);
+ wait_for_completion_timeout(&read_done, DS1WM_TIMEOUT);
+ ds1wm_data->read_complete = NULL;
+
+ return ds1wm_data->read_byte;
+}
+
+static int ds1wm_find_divisor(int gclk)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(freq); i++)
+ if (gclk <= freq[i].freq)
+ return freq[i].divisor;
+
+ return 0;
+}
+
+static void ds1wm_up(struct ds1wm_data *ds1wm_data)
+{
+ int gclk, divisor;
+
+ if (ds1wm_data->pdata->enable)
+ ds1wm_data->pdata->enable(ds1wm_data->pdev);
+
+ gclk = clk_get_rate(ds1wm_data->clk);
+ clk_enable(ds1wm_data->clk);
+ divisor = ds1wm_find_divisor(gclk);
+ if (divisor == 0) {
+ dev_err(&ds1wm_data->pdev->dev,
+ "no suitable divisor for %dHz clock\n", gclk);
+ return;
+ }
+ ds1wm_write_register(ds1wm_data, DS1WM_CLKDIV, divisor);
+
+ /* Let the w1 clock stabilize. */
+ msleep(1);
+
+ ds1wm_reset(ds1wm_data);
+}
+
+static void ds1wm_down(struct ds1wm_data *ds1wm_data)
+{
+ ds1wm_reset(ds1wm_data);
+
+ /* Disable interrupts. */
+ ds1wm_write_register(ds1wm_data, DS1WM_INT_EN,
+ ds1wm_data->active_high ? DS1WM_INTEN_IAS : 0);
+
+ if (ds1wm_data->pdata->disable)
+ ds1wm_data->pdata->disable(ds1wm_data->pdev);
+
+ clk_disable(ds1wm_data->clk);
+}
+
+/* --------------------------------------------------------------------- */
+/* w1 methods */
+
+static u8 ds1wm_read_byte(void *data)
+{
+ struct ds1wm_data *ds1wm_data = data;
+
+ return ds1wm_read(ds1wm_data, 0xff);
+}
+
+static void ds1wm_write_byte(void *data, u8 byte)
+{
+ struct ds1wm_data *ds1wm_data = data;
+
+ ds1wm_write(ds1wm_data, byte);
+}
+
+static u8 ds1wm_reset_bus(void *data)
+{
+ struct ds1wm_data *ds1wm_data = data;
+
+ ds1wm_reset(ds1wm_data);
+
+ return 0;
+}
+
+static void ds1wm_search(void *data, struct w1_master *master_dev,
+ u8 search_type, w1_slave_found_callback slave_found)
+{
+ struct ds1wm_data *ds1wm_data = data;
+ int i;
+ unsigned long long rom_id;
+
+ /* XXX We need to iterate for multiple devices per the DS1WM docs.
+ * See http://www.maxim-ic.com/appnotes.cfm/appnote_number/120. */
+ if (ds1wm_reset(ds1wm_data))
+ return;
+
+ ds1wm_write(ds1wm_data, search_type);
+ ds1wm_write_register(ds1wm_data, DS1WM_CMD, DS1WM_CMD_SRA);
+
+ for (rom_id = 0, i = 0; i < 16; i++) {
+
+ unsigned char resp, r, d;
+
+ resp = ds1wm_read(ds1wm_data, 0x00);
+
+ r = ((resp & 0x02) >> 1) |
+ ((resp & 0x08) >> 2) |
+ ((resp & 0x20) >> 3) |
+ ((resp & 0x80) >> 4);
+
+ d = ((resp & 0x01) >> 0) |
+ ((resp & 0x04) >> 1) |
+ ((resp & 0x10) >> 2) |
+ ((resp & 0x40) >> 3);
+
+ rom_id |= (unsigned long long) r << (i * 4);
+
+ }
+ dev_dbg(&ds1wm_data->pdev->dev, "found 0x%08llX\n", rom_id);
+
+ ds1wm_write_register(ds1wm_data, DS1WM_CMD, ~DS1WM_CMD_SRA);
+ ds1wm_reset(ds1wm_data);
+
+ slave_found(master_dev, rom_id);
+}
+
+/* --------------------------------------------------------------------- */
+
+static struct w1_bus_master ds1wm_master = {
+ .read_byte = ds1wm_read_byte,
+ .write_byte = ds1wm_write_byte,
+ .reset_bus = ds1wm_reset_bus,
+ .search = ds1wm_search,
+};
+
+static int ds1wm_probe(struct platform_device *pdev)
+{
+ struct ds1wm_data *ds1wm_data;
+ struct ds1wm_platform_data *plat;
+ struct resource *res;
+ int ret;
+
+ if (!pdev)
+ return -ENODEV;
+
+ ds1wm_data = kzalloc(sizeof(*ds1wm_data), GFP_KERNEL);
+ if (!ds1wm_data)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, ds1wm_data);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENXIO;
+ goto err0;
+ }
+ ds1wm_data->map = ioremap(res->start, res->end - res->start + 1);
+ if (!ds1wm_data->map) {
+ ret = -ENOMEM;
+ goto err0;
+ }
+ plat = pdev->dev.platform_data;
+ ds1wm_data->bus_shift = plat->bus_shift;
+ ds1wm_data->pdev = pdev;
+ ds1wm_data->pdata = plat;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ ret = -ENXIO;
+ goto err1;
+ }
+ ds1wm_data->irq = res->start;
+ ds1wm_data->active_high = plat->active_high;
+
+ if (res->flags & IORESOURCE_IRQ_HIGHEDGE)
+ set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_RISING);
+ if (res->flags & IORESOURCE_IRQ_LOWEDGE)
+ set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_FALLING);
+
+ ret = request_irq(ds1wm_data->irq, ds1wm_isr, IRQF_DISABLED,
+ "ds1wm", ds1wm_data);
+ if (ret)
+ goto err1;
+
+ ds1wm_data->clk = clk_get(&pdev->dev, "ds1wm");
+ if (IS_ERR(ds1wm_data->clk)) {
+ ret = PTR_ERR(ds1wm_data->clk);
+ goto err2;
+ }
+
+ ds1wm_up(ds1wm_data);
+
+ ds1wm_master.data = (void *)ds1wm_data;
+
+ ret = w1_add_master_device(&ds1wm_master);
+ if (ret)
+ goto err3;
+
+ return 0;
+
+err3:
+ ds1wm_down(ds1wm_data);
+ clk_put(ds1wm_data->clk);
+err2:
+ free_irq(ds1wm_data->irq, ds1wm_data);
+err1:
+ iounmap(ds1wm_data->map);
+err0:
+ kfree(ds1wm_data);
+
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int ds1wm_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct ds1wm_data *ds1wm_data = platform_get_drvdata(pdev);
+
+ ds1wm_down(ds1wm_data);
+
+ return 0;
+}
+
+static int ds1wm_resume(struct platform_device *pdev)
+{
+ struct ds1wm_data *ds1wm_data = platform_get_drvdata(pdev);
+
+ ds1wm_up(ds1wm_data);
+
+ return 0;
+}
+#else
+#define ds1wm_suspend NULL
+#define ds1wm_resume NULL
+#endif
+
+static int ds1wm_remove(struct platform_device *pdev)
+{
+ struct ds1wm_data *ds1wm_data = platform_get_drvdata(pdev);
+
+ w1_remove_master_device(&ds1wm_master);
+ ds1wm_down(ds1wm_data);
+ clk_put(ds1wm_data->clk);
+ free_irq(ds1wm_data->irq, ds1wm_data);
+ iounmap(ds1wm_data->map);
+ kfree(ds1wm_data);
+
+ return 0;
+}
+
+static struct platform_driver ds1wm_driver = {
+ .driver = {
+ .name = "ds1wm",
+ },
+ .probe = ds1wm_probe,
+ .remove = ds1wm_remove,
+ .suspend = ds1wm_suspend,
+ .resume = ds1wm_resume
+};
+
+static int __init ds1wm_init(void)
+{
+ printk("DS1WM w1 busmaster driver - (c) 2004 Szabolcs Gyurko\n");
+ return platform_driver_register(&ds1wm_driver);
+}
+
+static void __exit ds1wm_exit(void)
+{
+ platform_driver_unregister(&ds1wm_driver);
+}
+
+module_init(ds1wm_init);
+module_exit(ds1wm_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>, "
+ "Matt Reimer <mreimer@vpop.net>");
+MODULE_DESCRIPTION("DS1WM w1 busmaster driver");
diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c
new file mode 100644
index 0000000..df52cb3
--- /dev/null
+++ b/drivers/w1/masters/ds2482.c
@@ -0,0 +1,548 @@
+/**
+ * ds2482.c - provides i2c to w1-master bridge(s)
+ * Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com>
+ *
+ * The DS2482 is a sensor chip made by Dallas Semiconductor (Maxim).
+ * It is a I2C to 1-wire bridge.
+ * There are two variations: -100 and -800, which have 1 or 8 1-wire ports.
+ * The complete datasheet can be obtained from MAXIM's website at:
+ * http://www.maxim-ic.com/quick_view2.cfm/qv_pk/4382
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <asm/delay.h>
+
+#include "../w1.h"
+#include "../w1_int.h"
+
+/**
+ * Address is selected using 2 pins, resulting in 4 possible addresses.
+ * 0x18, 0x19, 0x1a, 0x1b
+ * However, the chip cannot be detected without doing an i2c write,
+ * so use the force module parameter.
+ */
+static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+/**
+ * Insmod parameters
+ */
+I2C_CLIENT_INSMOD_1(ds2482);
+
+/**
+ * The DS2482 registers - there are 3 registers that are addressed by a read
+ * pointer. The read pointer is set by the last command executed.
+ *
+ * To read the data, issue a register read for any address
+ */
+#define DS2482_CMD_RESET 0xF0 /* No param */
+#define DS2482_CMD_SET_READ_PTR 0xE1 /* Param: DS2482_PTR_CODE_xxx */
+#define DS2482_CMD_CHANNEL_SELECT 0xC3 /* Param: Channel byte - DS2482-800 only */
+#define DS2482_CMD_WRITE_CONFIG 0xD2 /* Param: Config byte */
+#define DS2482_CMD_1WIRE_RESET 0xB4 /* Param: None */
+#define DS2482_CMD_1WIRE_SINGLE_BIT 0x87 /* Param: Bit byte (bit7) */
+#define DS2482_CMD_1WIRE_WRITE_BYTE 0xA5 /* Param: Data byte */
+#define DS2482_CMD_1WIRE_READ_BYTE 0x96 /* Param: None */
+/* Note to read the byte, Set the ReadPtr to Data then read (any addr) */
+#define DS2482_CMD_1WIRE_TRIPLET 0x78 /* Param: Dir byte (bit7) */
+
+/* Values for DS2482_CMD_SET_READ_PTR */
+#define DS2482_PTR_CODE_STATUS 0xF0
+#define DS2482_PTR_CODE_DATA 0xE1
+#define DS2482_PTR_CODE_CHANNEL 0xD2 /* DS2482-800 only */
+#define DS2482_PTR_CODE_CONFIG 0xC3
+
+/**
+ * Configure Register bit definitions
+ * The top 4 bits always read 0.
+ * To write, the top nibble must be the 1's compl. of the low nibble.
+ */
+#define DS2482_REG_CFG_1WS 0x08
+#define DS2482_REG_CFG_SPU 0x04
+#define DS2482_REG_CFG_PPM 0x02
+#define DS2482_REG_CFG_APU 0x01
+
+
+/**
+ * Write and verify codes for the CHANNEL_SELECT command (DS2482-800 only).
+ * To set the channel, write the value at the index of the channel.
+ * Read and compare against the corresponding value to verify the change.
+ */
+static const u8 ds2482_chan_wr[8] =
+ { 0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87 };
+static const u8 ds2482_chan_rd[8] =
+ { 0xB8, 0xB1, 0xAA, 0xA3, 0x9C, 0x95, 0x8E, 0x87 };
+
+
+/**
+ * Status Register bit definitions (read only)
+ */
+#define DS2482_REG_STS_DIR 0x80
+#define DS2482_REG_STS_TSB 0x40
+#define DS2482_REG_STS_SBR 0x20
+#define DS2482_REG_STS_RST 0x10
+#define DS2482_REG_STS_LL 0x08
+#define DS2482_REG_STS_SD 0x04
+#define DS2482_REG_STS_PPD 0x02
+#define DS2482_REG_STS_1WB 0x01
+
+
+static int ds2482_probe(struct i2c_client *client,
+ const struct i2c_device_id *id);
+static int ds2482_detect(struct i2c_client *client, int kind,
+ struct i2c_board_info *info);
+static int ds2482_remove(struct i2c_client *client);
+
+
+/**
+ * Driver data (common to all clients)
+ */
+static const struct i2c_device_id ds2482_id[] = {
+ { "ds2482", 0 },
+ { }
+};
+
+static struct i2c_driver ds2482_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ds2482",
+ },
+ .probe = ds2482_probe,
+ .remove = ds2482_remove,
+ .id_table = ds2482_id,
+ .detect = ds2482_detect,
+ .address_data = &addr_data,
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct ds2482_data;
+
+struct ds2482_w1_chan {
+ struct ds2482_data *pdev;
+ u8 channel;
+ struct w1_bus_master w1_bm;
+};
+
+struct ds2482_data {
+ struct i2c_client *client;
+ struct mutex access_lock;
+
+ /* 1-wire interface(s) */
+ int w1_count; /* 1 or 8 */
+ struct ds2482_w1_chan w1_ch[8];
+
+ /* per-device values */
+ u8 channel;
+ u8 read_prt; /* see DS2482_PTR_CODE_xxx */
+ u8 reg_config;
+};
+
+
+/**
+ * Sets the read pointer.
+ * @param pdev The ds2482 client pointer
+ * @param read_ptr see DS2482_PTR_CODE_xxx above
+ * @return -1 on failure, 0 on success
+ */
+static inline int ds2482_select_register(struct ds2482_data *pdev, u8 read_ptr)
+{
+ if (pdev->read_prt != read_ptr) {
+ if (i2c_smbus_write_byte_data(pdev->client,
+ DS2482_CMD_SET_READ_PTR,
+ read_ptr) < 0)
+ return -1;
+
+ pdev->read_prt = read_ptr;
+ }
+ return 0;
+}
+
+/**
+ * Sends a command without a parameter
+ * @param pdev The ds2482 client pointer
+ * @param cmd DS2482_CMD_RESET,
+ * DS2482_CMD_1WIRE_RESET,
+ * DS2482_CMD_1WIRE_READ_BYTE
+ * @return -1 on failure, 0 on success
+ */
+static inline int ds2482_send_cmd(struct ds2482_data *pdev, u8 cmd)
+{
+ if (i2c_smbus_write_byte(pdev->client, cmd) < 0)
+ return -1;
+
+ pdev->read_prt = DS2482_PTR_CODE_STATUS;
+ return 0;
+}
+
+/**
+ * Sends a command with a parameter
+ * @param pdev The ds2482 client pointer
+ * @param cmd DS2482_CMD_WRITE_CONFIG,
+ * DS2482_CMD_1WIRE_SINGLE_BIT,
+ * DS2482_CMD_1WIRE_WRITE_BYTE,
+ * DS2482_CMD_1WIRE_TRIPLET
+ * @param byte The data to send
+ * @return -1 on failure, 0 on success
+ */
+static inline int ds2482_send_cmd_data(struct ds2482_data *pdev,
+ u8 cmd, u8 byte)
+{
+ if (i2c_smbus_write_byte_data(pdev->client, cmd, byte) < 0)
+ return -1;
+
+ /* all cmds leave in STATUS, except CONFIG */
+ pdev->read_prt = (cmd != DS2482_CMD_WRITE_CONFIG) ?
+ DS2482_PTR_CODE_STATUS : DS2482_PTR_CODE_CONFIG;
+ return 0;
+}
+
+
+/*
+ * 1-Wire interface code
+ */
+
+#define DS2482_WAIT_IDLE_TIMEOUT 100
+
+/**
+ * Waits until the 1-wire interface is idle (not busy)
+ *
+ * @param pdev Pointer to the device structure
+ * @return the last value read from status or -1 (failure)
+ */
+static int ds2482_wait_1wire_idle(struct ds2482_data *pdev)
+{
+ int temp = -1;
+ int retries = 0;
+
+ if (!ds2482_select_register(pdev, DS2482_PTR_CODE_STATUS)) {
+ do {
+ temp = i2c_smbus_read_byte(pdev->client);
+ } while ((temp >= 0) && (temp & DS2482_REG_STS_1WB) &&
+ (++retries < DS2482_WAIT_IDLE_TIMEOUT));
+ }
+
+ if (retries > DS2482_WAIT_IDLE_TIMEOUT)
+ printk(KERN_ERR "%s: timeout on channel %d\n",
+ __func__, pdev->channel);
+
+ return temp;
+}
+
+/**
+ * Selects a w1 channel.
+ * The 1-wire interface must be idle before calling this function.
+ *
+ * @param pdev The ds2482 client pointer
+ * @param channel 0-7
+ * @return -1 (failure) or 0 (success)
+ */
+static int ds2482_set_channel(struct ds2482_data *pdev, u8 channel)
+{
+ if (i2c_smbus_write_byte_data(pdev->client, DS2482_CMD_CHANNEL_SELECT,
+ ds2482_chan_wr[channel]) < 0)
+ return -1;
+
+ pdev->read_prt = DS2482_PTR_CODE_CHANNEL;
+ pdev->channel = -1;
+ if (i2c_smbus_read_byte(pdev->client) == ds2482_chan_rd[channel]) {
+ pdev->channel = channel;
+ return 0;
+ }
+ return -1;
+}
+
+
+/**
+ * Performs the touch-bit function, which writes a 0 or 1 and reads the level.
+ *
+ * @param data The ds2482 channel pointer
+ * @param bit The level to write: 0 or non-zero
+ * @return The level read: 0 or 1
+ */
+static u8 ds2482_w1_touch_bit(void *data, u8 bit)
+{
+ struct ds2482_w1_chan *pchan = data;
+ struct ds2482_data *pdev = pchan->pdev;
+ int status = -1;
+
+ mutex_lock(&pdev->access_lock);
+
+ /* Select the channel */
+ ds2482_wait_1wire_idle(pdev);
+ if (pdev->w1_count > 1)
+ ds2482_set_channel(pdev, pchan->channel);
+
+ /* Send the touch command, wait until 1WB == 0, return the status */
+ if (!ds2482_send_cmd_data(pdev, DS2482_CMD_1WIRE_SINGLE_BIT,
+ bit ? 0xFF : 0))
+ status = ds2482_wait_1wire_idle(pdev);
+
+ mutex_unlock(&pdev->access_lock);
+
+ return (status & DS2482_REG_STS_SBR) ? 1 : 0;
+}
+
+/**
+ * Performs the triplet function, which reads two bits and writes a bit.
+ * The bit written is determined by the two reads:
+ * 00 => dbit, 01 => 0, 10 => 1
+ *
+ * @param data The ds2482 channel pointer
+ * @param dbit The direction to choose if both branches are valid
+ * @return b0=read1 b1=read2 b3=bit written
+ */
+static u8 ds2482_w1_triplet(void *data, u8 dbit)
+{
+ struct ds2482_w1_chan *pchan = data;
+ struct ds2482_data *pdev = pchan->pdev;
+ int status = (3 << 5);
+
+ mutex_lock(&pdev->access_lock);
+
+ /* Select the channel */
+ ds2482_wait_1wire_idle(pdev);
+ if (pdev->w1_count > 1)
+ ds2482_set_channel(pdev, pchan->channel);
+
+ /* Send the triplet command, wait until 1WB == 0, return the status */
+ if (!ds2482_send_cmd_data(pdev, DS2482_CMD_1WIRE_TRIPLET,
+ dbit ? 0xFF : 0))
+ status = ds2482_wait_1wire_idle(pdev);
+
+ mutex_unlock(&pdev->access_lock);
+
+ /* Decode the status */
+ return (status >> 5);
+}
+
+/**
+ * Performs the write byte function.
+ *
+ * @param data The ds2482 channel pointer
+ * @param byte The value to write
+ */
+static void ds2482_w1_write_byte(void *data, u8 byte)
+{
+ struct ds2482_w1_chan *pchan = data;
+ struct ds2482_data *pdev = pchan->pdev;
+
+ mutex_lock(&pdev->access_lock);
+
+ /* Select the channel */
+ ds2482_wait_1wire_idle(pdev);
+ if (pdev->w1_count > 1)
+ ds2482_set_channel(pdev, pchan->channel);
+
+ /* Send the write byte command */
+ ds2482_send_cmd_data(pdev, DS2482_CMD_1WIRE_WRITE_BYTE, byte);
+
+ mutex_unlock(&pdev->access_lock);
+}
+
+/**
+ * Performs the read byte function.
+ *
+ * @param data The ds2482 channel pointer
+ * @return The value read
+ */
+static u8 ds2482_w1_read_byte(void *data)
+{
+ struct ds2482_w1_chan *pchan = data;
+ struct ds2482_data *pdev = pchan->pdev;
+ int result;
+
+ mutex_lock(&pdev->access_lock);
+
+ /* Select the channel */
+ ds2482_wait_1wire_idle(pdev);
+ if (pdev->w1_count > 1)
+ ds2482_set_channel(pdev, pchan->channel);
+
+ /* Send the read byte command */
+ ds2482_send_cmd(pdev, DS2482_CMD_1WIRE_READ_BYTE);
+
+ /* Wait until 1WB == 0 */
+ ds2482_wait_1wire_idle(pdev);
+
+ /* Select the data register */
+ ds2482_select_register(pdev, DS2482_PTR_CODE_DATA);
+
+ /* Read the data byte */
+ result = i2c_smbus_read_byte(pdev->client);
+
+ mutex_unlock(&pdev->access_lock);
+
+ return result;
+}
+
+
+/**
+ * Sends a reset on the 1-wire interface
+ *
+ * @param data The ds2482 channel pointer
+ * @return 0=Device present, 1=No device present or error
+ */
+static u8 ds2482_w1_reset_bus(void *data)
+{
+ struct ds2482_w1_chan *pchan = data;
+ struct ds2482_data *pdev = pchan->pdev;
+ int err;
+ u8 retval = 1;
+
+ mutex_lock(&pdev->access_lock);
+
+ /* Select the channel */
+ ds2482_wait_1wire_idle(pdev);
+ if (pdev->w1_count > 1)
+ ds2482_set_channel(pdev, pchan->channel);
+
+ /* Send the reset command */
+ err = ds2482_send_cmd(pdev, DS2482_CMD_1WIRE_RESET);
+ if (err >= 0) {
+ /* Wait until the reset is complete */
+ err = ds2482_wait_1wire_idle(pdev);
+ retval = !(err & DS2482_REG_STS_PPD);
+
+ /* If the chip did reset since detect, re-config it */
+ if (err & DS2482_REG_STS_RST)
+ ds2482_send_cmd_data(pdev, DS2482_CMD_WRITE_CONFIG,
+ 0xF0);
+ }
+
+ mutex_unlock(&pdev->access_lock);
+
+ return retval;
+}
+
+
+static int ds2482_detect(struct i2c_client *client, int kind,
+ struct i2c_board_info *info)
+{
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
+ I2C_FUNC_SMBUS_BYTE))
+ return -ENODEV;
+
+ strlcpy(info->type, "ds2482", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int ds2482_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ds2482_data *data;
+ int err = -ENODEV;
+ int temp1;
+ int idx;
+
+ if (!(data = kzalloc(sizeof(struct ds2482_data), GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ data->client = client;
+ i2c_set_clientdata(client, data);
+
+ /* Reset the device (sets the read_ptr to status) */
+ if (ds2482_send_cmd(data, DS2482_CMD_RESET) < 0) {
+ dev_warn(&client->dev, "DS2482 reset failed.\n");
+ goto exit_free;
+ }
+
+ /* Sleep at least 525ns to allow the reset to complete */
+ ndelay(525);
+
+ /* Read the status byte - only reset bit and line should be set */
+ temp1 = i2c_smbus_read_byte(client);
+ if (temp1 != (DS2482_REG_STS_LL | DS2482_REG_STS_RST)) {
+ dev_warn(&client->dev, "DS2482 reset status "
+ "0x%02X - not a DS2482\n", temp1);
+ goto exit_free;
+ }
+
+ /* Detect the 8-port version */
+ data->w1_count = 1;
+ if (ds2482_set_channel(data, 7) == 0)
+ data->w1_count = 8;
+
+ /* Set all config items to 0 (off) */
+ ds2482_send_cmd_data(data, DS2482_CMD_WRITE_CONFIG, 0xF0);
+
+ mutex_init(&data->access_lock);
+
+ /* Register 1-wire interface(s) */
+ for (idx = 0; idx < data->w1_count; idx++) {
+ data->w1_ch[idx].pdev = data;
+ data->w1_ch[idx].channel = idx;
+
+ /* Populate all the w1 bus master stuff */
+ data->w1_ch[idx].w1_bm.data = &data->w1_ch[idx];
+ data->w1_ch[idx].w1_bm.read_byte = ds2482_w1_read_byte;
+ data->w1_ch[idx].w1_bm.write_byte = ds2482_w1_write_byte;
+ data->w1_ch[idx].w1_bm.touch_bit = ds2482_w1_touch_bit;
+ data->w1_ch[idx].w1_bm.triplet = ds2482_w1_triplet;
+ data->w1_ch[idx].w1_bm.reset_bus = ds2482_w1_reset_bus;
+
+ err = w1_add_master_device(&data->w1_ch[idx].w1_bm);
+ if (err) {
+ data->w1_ch[idx].pdev = NULL;
+ goto exit_w1_remove;
+ }
+ }
+
+ return 0;
+
+exit_w1_remove:
+ for (idx = 0; idx < data->w1_count; idx++) {
+ if (data->w1_ch[idx].pdev != NULL)
+ w1_remove_master_device(&data->w1_ch[idx].w1_bm);
+ }
+exit_free:
+ kfree(data);
+exit:
+ return err;
+}
+
+static int ds2482_remove(struct i2c_client *client)
+{
+ struct ds2482_data *data = i2c_get_clientdata(client);
+ int idx;
+
+ /* Unregister the 1-wire bridge(s) */
+ for (idx = 0; idx < data->w1_count; idx++) {
+ if (data->w1_ch[idx].pdev != NULL)
+ w1_remove_master_device(&data->w1_ch[idx].w1_bm);
+ }
+
+ /* Free the memory */
+ kfree(data);
+ return 0;
+}
+
+static int __init sensors_ds2482_init(void)
+{
+ return i2c_add_driver(&ds2482_driver);
+}
+
+static void __exit sensors_ds2482_exit(void)
+{
+ i2c_del_driver(&ds2482_driver);
+}
+
+MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>");
+MODULE_DESCRIPTION("DS2482 driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_ds2482_init);
+module_exit(sensors_ds2482_exit);
diff --git a/drivers/w1/masters/ds2490.c b/drivers/w1/masters/ds2490.c
new file mode 100644
index 0000000..59ad6e9
--- /dev/null
+++ b/drivers/w1/masters/ds2490.c
@@ -0,0 +1,1027 @@
+/*
+ * dscore.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms 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/module.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/usb.h>
+
+#include "../w1_int.h"
+#include "../w1.h"
+
+/* COMMAND TYPE CODES */
+#define CONTROL_CMD 0x00
+#define COMM_CMD 0x01
+#define MODE_CMD 0x02
+
+/* CONTROL COMMAND CODES */
+#define CTL_RESET_DEVICE 0x0000
+#define CTL_START_EXE 0x0001
+#define CTL_RESUME_EXE 0x0002
+#define CTL_HALT_EXE_IDLE 0x0003
+#define CTL_HALT_EXE_DONE 0x0004
+#define CTL_FLUSH_COMM_CMDS 0x0007
+#define CTL_FLUSH_RCV_BUFFER 0x0008
+#define CTL_FLUSH_XMT_BUFFER 0x0009
+#define CTL_GET_COMM_CMDS 0x000A
+
+/* MODE COMMAND CODES */
+#define MOD_PULSE_EN 0x0000
+#define MOD_SPEED_CHANGE_EN 0x0001
+#define MOD_1WIRE_SPEED 0x0002
+#define MOD_STRONG_PU_DURATION 0x0003
+#define MOD_PULLDOWN_SLEWRATE 0x0004
+#define MOD_PROG_PULSE_DURATION 0x0005
+#define MOD_WRITE1_LOWTIME 0x0006
+#define MOD_DSOW0_TREC 0x0007
+
+/* COMMUNICATION COMMAND CODES */
+#define COMM_ERROR_ESCAPE 0x0601
+#define COMM_SET_DURATION 0x0012
+#define COMM_BIT_IO 0x0020
+#define COMM_PULSE 0x0030
+#define COMM_1_WIRE_RESET 0x0042
+#define COMM_BYTE_IO 0x0052
+#define COMM_MATCH_ACCESS 0x0064
+#define COMM_BLOCK_IO 0x0074
+#define COMM_READ_STRAIGHT 0x0080
+#define COMM_DO_RELEASE 0x6092
+#define COMM_SET_PATH 0x00A2
+#define COMM_WRITE_SRAM_PAGE 0x00B2
+#define COMM_WRITE_EPROM 0x00C4
+#define COMM_READ_CRC_PROT_PAGE 0x00D4
+#define COMM_READ_REDIRECT_PAGE_CRC 0x21E4
+#define COMM_SEARCH_ACCESS 0x00F4
+
+/* Communication command bits */
+#define COMM_TYPE 0x0008
+#define COMM_SE 0x0008
+#define COMM_D 0x0008
+#define COMM_Z 0x0008
+#define COMM_CH 0x0008
+#define COMM_SM 0x0008
+#define COMM_R 0x0008
+#define COMM_IM 0x0001
+
+#define COMM_PS 0x4000
+#define COMM_PST 0x4000
+#define COMM_CIB 0x4000
+#define COMM_RTS 0x4000
+#define COMM_DT 0x2000
+#define COMM_SPU 0x1000
+#define COMM_F 0x0800
+#define COMM_NTF 0x0400
+#define COMM_ICP 0x0200
+#define COMM_RST 0x0100
+
+#define PULSE_PROG 0x01
+#define PULSE_SPUE 0x02
+
+#define BRANCH_MAIN 0xCC
+#define BRANCH_AUX 0x33
+
+/* Status flags */
+#define ST_SPUA 0x01 /* Strong Pull-up is active */
+#define ST_PRGA 0x02 /* 12V programming pulse is being generated */
+#define ST_12VP 0x04 /* external 12V programming voltage is present */
+#define ST_PMOD 0x08 /* DS2490 powered from USB and external sources */
+#define ST_HALT 0x10 /* DS2490 is currently halted */
+#define ST_IDLE 0x20 /* DS2490 is currently idle */
+#define ST_EPOF 0x80
+
+/* Result Register flags */
+#define RR_DETECT 0xA5 /* New device detected */
+#define RR_NRS 0x01 /* Reset no presence or ... */
+#define RR_SH 0x02 /* short on reset or set path */
+#define RR_APP 0x04 /* alarming presence on reset */
+#define RR_VPP 0x08 /* 12V expected not seen */
+#define RR_CMP 0x10 /* compare error */
+#define RR_CRC 0x20 /* CRC error detected */
+#define RR_RDP 0x40 /* redirected page */
+#define RR_EOS 0x80 /* end of search error */
+
+#define SPEED_NORMAL 0x00
+#define SPEED_FLEXIBLE 0x01
+#define SPEED_OVERDRIVE 0x02
+
+#define NUM_EP 4
+#define EP_CONTROL 0
+#define EP_STATUS 1
+#define EP_DATA_OUT 2
+#define EP_DATA_IN 3
+
+struct ds_device
+{
+ struct list_head ds_entry;
+
+ struct usb_device *udev;
+ struct usb_interface *intf;
+
+ int ep[NUM_EP];
+
+ /* Strong PullUp
+ * 0: pullup not active, else duration in milliseconds
+ */
+ int spu_sleep;
+ /* spu_bit contains COMM_SPU or 0 depending on if the strong pullup
+ * should be active or not for writes.
+ */
+ u16 spu_bit;
+
+ struct w1_bus_master master;
+};
+
+struct ds_status
+{
+ u8 enable;
+ u8 speed;
+ u8 pullup_dur;
+ u8 ppuls_dur;
+ u8 pulldown_slew;
+ u8 write1_time;
+ u8 write0_time;
+ u8 reserved0;
+ u8 status;
+ u8 command0;
+ u8 command1;
+ u8 command_buffer_status;
+ u8 data_out_buffer_status;
+ u8 data_in_buffer_status;
+ u8 reserved1;
+ u8 reserved2;
+
+};
+
+static struct usb_device_id ds_id_table [] = {
+ { USB_DEVICE(0x04fa, 0x2490) },
+ { },
+};
+MODULE_DEVICE_TABLE(usb, ds_id_table);
+
+static int ds_probe(struct usb_interface *, const struct usb_device_id *);
+static void ds_disconnect(struct usb_interface *);
+
+static int ds_send_control(struct ds_device *, u16, u16);
+static int ds_send_control_cmd(struct ds_device *, u16, u16);
+
+static LIST_HEAD(ds_devices);
+static DEFINE_MUTEX(ds_mutex);
+
+static struct usb_driver ds_driver = {
+ .name = "DS9490R",
+ .probe = ds_probe,
+ .disconnect = ds_disconnect,
+ .id_table = ds_id_table,
+};
+
+static int ds_send_control_cmd(struct ds_device *dev, u16 value, u16 index)
+{
+ int err;
+
+ err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]),
+ CONTROL_CMD, 0x40, value, index, NULL, 0, 1000);
+ if (err < 0) {
+ printk(KERN_ERR "Failed to send command control message %x.%x: err=%d.\n",
+ value, index, err);
+ return err;
+ }
+
+ return err;
+}
+
+static int ds_send_control_mode(struct ds_device *dev, u16 value, u16 index)
+{
+ int err;
+
+ err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]),
+ MODE_CMD, 0x40, value, index, NULL, 0, 1000);
+ if (err < 0) {
+ printk(KERN_ERR "Failed to send mode control message %x.%x: err=%d.\n",
+ value, index, err);
+ return err;
+ }
+
+ return err;
+}
+
+static int ds_send_control(struct ds_device *dev, u16 value, u16 index)
+{
+ int err;
+
+ err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]),
+ COMM_CMD, 0x40, value, index, NULL, 0, 1000);
+ if (err < 0) {
+ printk(KERN_ERR "Failed to send control message %x.%x: err=%d.\n",
+ value, index, err);
+ return err;
+ }
+
+ return err;
+}
+
+static int ds_recv_status_nodump(struct ds_device *dev, struct ds_status *st,
+ unsigned char *buf, int size)
+{
+ int count, err;
+
+ memset(st, 0, sizeof(*st));
+
+ count = 0;
+ err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_STATUS]), buf, size, &count, 100);
+ if (err < 0) {
+ printk(KERN_ERR "Failed to read 1-wire data from 0x%x: err=%d.\n", dev->ep[EP_STATUS], err);
+ return err;
+ }
+
+ if (count >= sizeof(*st))
+ memcpy(st, buf, sizeof(*st));
+
+ return count;
+}
+
+static inline void ds_print_msg(unsigned char *buf, unsigned char *str, int off)
+{
+ printk(KERN_INFO "%45s: %8x\n", str, buf[off]);
+}
+
+static void ds_dump_status(struct ds_device *dev, unsigned char *buf, int count)
+{
+ int i;
+
+ printk(KERN_INFO "0x%x: count=%d, status: ", dev->ep[EP_STATUS], count);
+ for (i=0; i<count; ++i)
+ printk("%02x ", buf[i]);
+ printk(KERN_INFO "\n");
+
+ if (count >= 16) {
+ ds_print_msg(buf, "enable flag", 0);
+ ds_print_msg(buf, "1-wire speed", 1);
+ ds_print_msg(buf, "strong pullup duration", 2);
+ ds_print_msg(buf, "programming pulse duration", 3);
+ ds_print_msg(buf, "pulldown slew rate control", 4);
+ ds_print_msg(buf, "write-1 low time", 5);
+ ds_print_msg(buf, "data sample offset/write-0 recovery time",
+ 6);
+ ds_print_msg(buf, "reserved (test register)", 7);
+ ds_print_msg(buf, "device status flags", 8);
+ ds_print_msg(buf, "communication command byte 1", 9);
+ ds_print_msg(buf, "communication command byte 2", 10);
+ ds_print_msg(buf, "communication command buffer status", 11);
+ ds_print_msg(buf, "1-wire data output buffer status", 12);
+ ds_print_msg(buf, "1-wire data input buffer status", 13);
+ ds_print_msg(buf, "reserved", 14);
+ ds_print_msg(buf, "reserved", 15);
+ }
+ for (i = 16; i < count; ++i) {
+ if (buf[i] == RR_DETECT) {
+ ds_print_msg(buf, "new device detect", i);
+ continue;
+ }
+ ds_print_msg(buf, "Result Register Value: ", i);
+ if (buf[i] & RR_NRS)
+ printk(KERN_INFO "NRS: Reset no presence or ...\n");
+ if (buf[i] & RR_SH)
+ printk(KERN_INFO "SH: short on reset or set path\n");
+ if (buf[i] & RR_APP)
+ printk(KERN_INFO "APP: alarming presence on reset\n");
+ if (buf[i] & RR_VPP)
+ printk(KERN_INFO "VPP: 12V expected not seen\n");
+ if (buf[i] & RR_CMP)
+ printk(KERN_INFO "CMP: compare error\n");
+ if (buf[i] & RR_CRC)
+ printk(KERN_INFO "CRC: CRC error detected\n");
+ if (buf[i] & RR_RDP)
+ printk(KERN_INFO "RDP: redirected page\n");
+ if (buf[i] & RR_EOS)
+ printk(KERN_INFO "EOS: end of search error\n");
+ }
+}
+
+static void ds_reset_device(struct ds_device *dev)
+{
+ ds_send_control_cmd(dev, CTL_RESET_DEVICE, 0);
+ /* Always allow strong pullup which allow individual writes to use
+ * the strong pullup.
+ */
+ if (ds_send_control_mode(dev, MOD_PULSE_EN, PULSE_SPUE))
+ printk(KERN_ERR "ds_reset_device: "
+ "Error allowing strong pullup\n");
+ /* Chip strong pullup time was cleared. */
+ if (dev->spu_sleep) {
+ /* lower 4 bits are 0, see ds_set_pullup */
+ u8 del = dev->spu_sleep>>4;
+ if (ds_send_control(dev, COMM_SET_DURATION | COMM_IM, del))
+ printk(KERN_ERR "ds_reset_device: "
+ "Error setting duration\n");
+ }
+}
+
+static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size)
+{
+ int count, err;
+ struct ds_status st;
+
+ /* Careful on size. If size is less than what is available in
+ * the input buffer, the device fails the bulk transfer and
+ * clears the input buffer. It could read the maximum size of
+ * the data buffer, but then do you return the first, last, or
+ * some set of the middle size bytes? As long as the rest of
+ * the code is correct there will be size bytes waiting. A
+ * call to ds_wait_status will wait until the device is idle
+ * and any data to be received would have been available.
+ */
+ count = 0;
+ err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN]),
+ buf, size, &count, 1000);
+ if (err < 0) {
+ u8 buf[0x20];
+ int count;
+
+ printk(KERN_INFO "Clearing ep0x%x.\n", dev->ep[EP_DATA_IN]);
+ usb_clear_halt(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN]));
+
+ count = ds_recv_status_nodump(dev, &st, buf, sizeof(buf));
+ ds_dump_status(dev, buf, count);
+ return err;
+ }
+
+#if 0
+ {
+ int i;
+
+ printk("%s: count=%d: ", __func__, count);
+ for (i=0; i<count; ++i)
+ printk("%02x ", buf[i]);
+ printk("\n");
+ }
+#endif
+ return count;
+}
+
+static int ds_send_data(struct ds_device *dev, unsigned char *buf, int len)
+{
+ int count, err;
+
+ count = 0;
+ err = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, dev->ep[EP_DATA_OUT]), buf, len, &count, 1000);
+ if (err < 0) {
+ printk(KERN_ERR "Failed to write 1-wire data to ep0x%x: "
+ "err=%d.\n", dev->ep[EP_DATA_OUT], err);
+ return err;
+ }
+
+ return err;
+}
+
+#if 0
+
+int ds_stop_pulse(struct ds_device *dev, int limit)
+{
+ struct ds_status st;
+ int count = 0, err = 0;
+ u8 buf[0x20];
+
+ do {
+ err = ds_send_control(dev, CTL_HALT_EXE_IDLE, 0);
+ if (err)
+ break;
+ err = ds_send_control(dev, CTL_RESUME_EXE, 0);
+ if (err)
+ break;
+ err = ds_recv_status_nodump(dev, &st, buf, sizeof(buf));
+ if (err)
+ break;
+
+ if ((st.status & ST_SPUA) == 0) {
+ err = ds_send_control_mode(dev, MOD_PULSE_EN, 0);
+ if (err)
+ break;
+ }
+ } while(++count < limit);
+
+ return err;
+}
+
+int ds_detect(struct ds_device *dev, struct ds_status *st)
+{
+ int err;
+
+ err = ds_send_control_cmd(dev, CTL_RESET_DEVICE, 0);
+ if (err)
+ return err;
+
+ err = ds_send_control(dev, COMM_SET_DURATION | COMM_IM, 0);
+ if (err)
+ return err;
+
+ err = ds_send_control(dev, COMM_SET_DURATION | COMM_IM | COMM_TYPE, 0x40);
+ if (err)
+ return err;
+
+ err = ds_send_control_mode(dev, MOD_PULSE_EN, PULSE_PROG);
+ if (err)
+ return err;
+
+ err = ds_dump_status(dev, st);
+
+ return err;
+}
+
+#endif /* 0 */
+
+static int ds_wait_status(struct ds_device *dev, struct ds_status *st)
+{
+ u8 buf[0x20];
+ int err, count = 0;
+
+ do {
+ err = ds_recv_status_nodump(dev, st, buf, sizeof(buf));
+#if 0
+ if (err >= 0) {
+ int i;
+ printk("0x%x: count=%d, status: ", dev->ep[EP_STATUS], err);
+ for (i=0; i<err; ++i)
+ printk("%02x ", buf[i]);
+ printk("\n");
+ }
+#endif
+ } while (!(buf[0x08] & ST_IDLE) && !(err < 0) && ++count < 100);
+
+ if (err >= 16 && st->status & ST_EPOF) {
+ printk(KERN_INFO "Resetting device after ST_EPOF.\n");
+ ds_reset_device(dev);
+ /* Always dump the device status. */
+ count = 101;
+ }
+
+ /* Dump the status for errors or if there is extended return data.
+ * The extended status includes new device detection (maybe someone
+ * can do something with it).
+ */
+ if (err > 16 || count >= 100 || err < 0)
+ ds_dump_status(dev, buf, err);
+
+ /* Extended data isn't an error. Well, a short is, but the dump
+ * would have already told the user that and we can't do anything
+ * about it in software anyway.
+ */
+ if (count >= 100 || err < 0)
+ return -1;
+ else
+ return 0;
+}
+
+static int ds_reset(struct ds_device *dev)
+{
+ int err;
+
+ /* Other potentionally interesting flags for reset.
+ *
+ * COMM_NTF: Return result register feedback. This could be used to
+ * detect some conditions such as short, alarming presence, or
+ * detect if a new device was detected.
+ *
+ * COMM_SE which allows SPEED_NORMAL, SPEED_FLEXIBLE, SPEED_OVERDRIVE:
+ * Select the data transfer rate.
+ */
+ err = ds_send_control(dev, COMM_1_WIRE_RESET | COMM_IM, SPEED_NORMAL);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+#if 0
+static int ds_set_speed(struct ds_device *dev, int speed)
+{
+ int err;
+
+ if (speed != SPEED_NORMAL && speed != SPEED_FLEXIBLE && speed != SPEED_OVERDRIVE)
+ return -EINVAL;
+
+ if (speed != SPEED_OVERDRIVE)
+ speed = SPEED_FLEXIBLE;
+
+ speed &= 0xff;
+
+ err = ds_send_control_mode(dev, MOD_1WIRE_SPEED, speed);
+ if (err)
+ return err;
+
+ return err;
+}
+#endif /* 0 */
+
+static int ds_set_pullup(struct ds_device *dev, int delay)
+{
+ int err = 0;
+ u8 del = 1 + (u8)(delay >> 4);
+ /* Just storing delay would not get the trunication and roundup. */
+ int ms = del<<4;
+
+ /* Enable spu_bit if a delay is set. */
+ dev->spu_bit = delay ? COMM_SPU : 0;
+ /* If delay is zero, it has already been disabled, if the time is
+ * the same as the hardware was last programmed to, there is also
+ * nothing more to do. Compare with the recalculated value ms
+ * rather than del or delay which can have a different value.
+ */
+ if (delay == 0 || ms == dev->spu_sleep)
+ return err;
+
+ err = ds_send_control(dev, COMM_SET_DURATION | COMM_IM, del);
+ if (err)
+ return err;
+
+ dev->spu_sleep = ms;
+
+ return err;
+}
+
+static int ds_touch_bit(struct ds_device *dev, u8 bit, u8 *tbit)
+{
+ int err;
+ struct ds_status st;
+
+ err = ds_send_control(dev, COMM_BIT_IO | COMM_IM | (bit ? COMM_D : 0),
+ 0);
+ if (err)
+ return err;
+
+ ds_wait_status(dev, &st);
+
+ err = ds_recv_data(dev, tbit, sizeof(*tbit));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+#if 0
+static int ds_write_bit(struct ds_device *dev, u8 bit)
+{
+ int err;
+ struct ds_status st;
+
+ /* Set COMM_ICP to write without a readback. Note, this will
+ * produce one time slot, a down followed by an up with COMM_D
+ * only determing the timing.
+ */
+ err = ds_send_control(dev, COMM_BIT_IO | COMM_IM | COMM_ICP |
+ (bit ? COMM_D : 0), 0);
+ if (err)
+ return err;
+
+ ds_wait_status(dev, &st);
+
+ return 0;
+}
+#endif
+
+static int ds_write_byte(struct ds_device *dev, u8 byte)
+{
+ int err;
+ struct ds_status st;
+ u8 rbyte;
+
+ err = ds_send_control(dev, COMM_BYTE_IO | COMM_IM | dev->spu_bit, byte);
+ if (err)
+ return err;
+
+ if (dev->spu_bit)
+ msleep(dev->spu_sleep);
+
+ err = ds_wait_status(dev, &st);
+ if (err)
+ return err;
+
+ err = ds_recv_data(dev, &rbyte, sizeof(rbyte));
+ if (err < 0)
+ return err;
+
+ return !(byte == rbyte);
+}
+
+static int ds_read_byte(struct ds_device *dev, u8 *byte)
+{
+ int err;
+ struct ds_status st;
+
+ err = ds_send_control(dev, COMM_BYTE_IO | COMM_IM , 0xff);
+ if (err)
+ return err;
+
+ ds_wait_status(dev, &st);
+
+ err = ds_recv_data(dev, byte, sizeof(*byte));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int ds_read_block(struct ds_device *dev, u8 *buf, int len)
+{
+ struct ds_status st;
+ int err;
+
+ if (len > 64*1024)
+ return -E2BIG;
+
+ memset(buf, 0xFF, len);
+
+ err = ds_send_data(dev, buf, len);
+ if (err < 0)
+ return err;
+
+ err = ds_send_control(dev, COMM_BLOCK_IO | COMM_IM, len);
+ if (err)
+ return err;
+
+ ds_wait_status(dev, &st);
+
+ memset(buf, 0x00, len);
+ err = ds_recv_data(dev, buf, len);
+
+ return err;
+}
+
+static int ds_write_block(struct ds_device *dev, u8 *buf, int len)
+{
+ int err;
+ struct ds_status st;
+
+ err = ds_send_data(dev, buf, len);
+ if (err < 0)
+ return err;
+
+ err = ds_send_control(dev, COMM_BLOCK_IO | COMM_IM | dev->spu_bit, len);
+ if (err)
+ return err;
+
+ if (dev->spu_bit)
+ msleep(dev->spu_sleep);
+
+ ds_wait_status(dev, &st);
+
+ err = ds_recv_data(dev, buf, len);
+ if (err < 0)
+ return err;
+
+ return !(err == len);
+}
+
+#if 0
+
+static int ds_search(struct ds_device *dev, u64 init, u64 *buf, u8 id_number, int conditional_search)
+{
+ int err;
+ u16 value, index;
+ struct ds_status st;
+
+ memset(buf, 0, sizeof(buf));
+
+ err = ds_send_data(ds_dev, (unsigned char *)&init, 8);
+ if (err)
+ return err;
+
+ ds_wait_status(ds_dev, &st);
+
+ value = COMM_SEARCH_ACCESS | COMM_IM | COMM_SM | COMM_F | COMM_RTS;
+ index = (conditional_search ? 0xEC : 0xF0) | (id_number << 8);
+ err = ds_send_control(ds_dev, value, index);
+ if (err)
+ return err;
+
+ ds_wait_status(ds_dev, &st);
+
+ err = ds_recv_data(ds_dev, (unsigned char *)buf, 8*id_number);
+ if (err < 0)
+ return err;
+
+ return err/8;
+}
+
+static int ds_match_access(struct ds_device *dev, u64 init)
+{
+ int err;
+ struct ds_status st;
+
+ err = ds_send_data(dev, (unsigned char *)&init, sizeof(init));
+ if (err)
+ return err;
+
+ ds_wait_status(dev, &st);
+
+ err = ds_send_control(dev, COMM_MATCH_ACCESS | COMM_IM | COMM_RST, 0x0055);
+ if (err)
+ return err;
+
+ ds_wait_status(dev, &st);
+
+ return 0;
+}
+
+static int ds_set_path(struct ds_device *dev, u64 init)
+{
+ int err;
+ struct ds_status st;
+ u8 buf[9];
+
+ memcpy(buf, &init, 8);
+ buf[8] = BRANCH_MAIN;
+
+ err = ds_send_data(dev, buf, sizeof(buf));
+ if (err)
+ return err;
+
+ ds_wait_status(dev, &st);
+
+ err = ds_send_control(dev, COMM_SET_PATH | COMM_IM | COMM_RST, 0);
+ if (err)
+ return err;
+
+ ds_wait_status(dev, &st);
+
+ return 0;
+}
+
+#endif /* 0 */
+
+static u8 ds9490r_touch_bit(void *data, u8 bit)
+{
+ u8 ret;
+ struct ds_device *dev = data;
+
+ if (ds_touch_bit(dev, bit, &ret))
+ return 0;
+
+ return ret;
+}
+
+#if 0
+static void ds9490r_write_bit(void *data, u8 bit)
+{
+ struct ds_device *dev = data;
+
+ ds_write_bit(dev, bit);
+}
+
+static u8 ds9490r_read_bit(void *data)
+{
+ struct ds_device *dev = data;
+ int err;
+ u8 bit = 0;
+
+ err = ds_touch_bit(dev, 1, &bit);
+ if (err)
+ return 0;
+
+ return bit & 1;
+}
+#endif
+
+static void ds9490r_write_byte(void *data, u8 byte)
+{
+ struct ds_device *dev = data;
+
+ ds_write_byte(dev, byte);
+}
+
+static u8 ds9490r_read_byte(void *data)
+{
+ struct ds_device *dev = data;
+ int err;
+ u8 byte = 0;
+
+ err = ds_read_byte(dev, &byte);
+ if (err)
+ return 0;
+
+ return byte;
+}
+
+static void ds9490r_write_block(void *data, const u8 *buf, int len)
+{
+ struct ds_device *dev = data;
+
+ ds_write_block(dev, (u8 *)buf, len);
+}
+
+static u8 ds9490r_read_block(void *data, u8 *buf, int len)
+{
+ struct ds_device *dev = data;
+ int err;
+
+ err = ds_read_block(dev, buf, len);
+ if (err < 0)
+ return 0;
+
+ return len;
+}
+
+static u8 ds9490r_reset(void *data)
+{
+ struct ds_device *dev = data;
+ int err;
+
+ err = ds_reset(dev);
+ if (err)
+ return 1;
+
+ return 0;
+}
+
+static u8 ds9490r_set_pullup(void *data, int delay)
+{
+ struct ds_device *dev = data;
+
+ if (ds_set_pullup(dev, delay))
+ return 1;
+
+ return 0;
+}
+
+static int ds_w1_init(struct ds_device *dev)
+{
+ memset(&dev->master, 0, sizeof(struct w1_bus_master));
+
+ /* Reset the device as it can be in a bad state.
+ * This is necessary because a block write will wait for data
+ * to be placed in the output buffer and block any later
+ * commands which will keep accumulating and the device will
+ * not be idle. Another case is removing the ds2490 module
+ * while a bus search is in progress, somehow a few commands
+ * get through, but the input transfers fail leaving data in
+ * the input buffer. This will cause the next read to fail
+ * see the note in ds_recv_data.
+ */
+ ds_reset_device(dev);
+
+ dev->master.data = dev;
+ dev->master.touch_bit = &ds9490r_touch_bit;
+ /* read_bit and write_bit in w1_bus_master are expected to set and
+ * sample the line level. For write_bit that means it is expected to
+ * set it to that value and leave it there. ds2490 only supports an
+ * individual time slot at the lowest level. The requirement from
+ * pulling the bus state down to reading the state is 15us, something
+ * that isn't realistic on the USB bus anyway.
+ dev->master.read_bit = &ds9490r_read_bit;
+ dev->master.write_bit = &ds9490r_write_bit;
+ */
+ dev->master.read_byte = &ds9490r_read_byte;
+ dev->master.write_byte = &ds9490r_write_byte;
+ dev->master.read_block = &ds9490r_read_block;
+ dev->master.write_block = &ds9490r_write_block;
+ dev->master.reset_bus = &ds9490r_reset;
+ dev->master.set_pullup = &ds9490r_set_pullup;
+
+ return w1_add_master_device(&dev->master);
+}
+
+static void ds_w1_fini(struct ds_device *dev)
+{
+ w1_remove_master_device(&dev->master);
+}
+
+static int ds_probe(struct usb_interface *intf,
+ const struct usb_device_id *udev_id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_host_interface *iface_desc;
+ struct ds_device *dev;
+ int i, err;
+
+ dev = kmalloc(sizeof(struct ds_device), GFP_KERNEL);
+ if (!dev) {
+ printk(KERN_INFO "Failed to allocate new DS9490R structure.\n");
+ return -ENOMEM;
+ }
+ dev->spu_sleep = 0;
+ dev->spu_bit = 0;
+ dev->udev = usb_get_dev(udev);
+ if (!dev->udev) {
+ err = -ENOMEM;
+ goto err_out_free;
+ }
+ memset(dev->ep, 0, sizeof(dev->ep));
+
+ usb_set_intfdata(intf, dev);
+
+ err = usb_set_interface(dev->udev, intf->altsetting[0].desc.bInterfaceNumber, 3);
+ if (err) {
+ printk(KERN_ERR "Failed to set alternative setting 3 for %d interface: err=%d.\n",
+ intf->altsetting[0].desc.bInterfaceNumber, err);
+ goto err_out_clear;
+ }
+
+ err = usb_reset_configuration(dev->udev);
+ if (err) {
+ printk(KERN_ERR "Failed to reset configuration: err=%d.\n", err);
+ goto err_out_clear;
+ }
+
+ iface_desc = &intf->altsetting[0];
+ if (iface_desc->desc.bNumEndpoints != NUM_EP-1) {
+ printk(KERN_INFO "Num endpoints=%d. It is not DS9490R.\n", iface_desc->desc.bNumEndpoints);
+ err = -EINVAL;
+ goto err_out_clear;
+ }
+
+ /*
+ * This loop doesn'd show control 0 endpoint,
+ * so we will fill only 1-3 endpoints entry.
+ */
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ dev->ep[i+1] = endpoint->bEndpointAddress;
+#if 0
+ printk("%d: addr=%x, size=%d, dir=%s, type=%x\n",
+ i, endpoint->bEndpointAddress, le16_to_cpu(endpoint->wMaxPacketSize),
+ (endpoint->bEndpointAddress & USB_DIR_IN)?"IN":"OUT",
+ endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
+#endif
+ }
+
+ err = ds_w1_init(dev);
+ if (err)
+ goto err_out_clear;
+
+ mutex_lock(&ds_mutex);
+ list_add_tail(&dev->ds_entry, &ds_devices);
+ mutex_unlock(&ds_mutex);
+
+ return 0;
+
+err_out_clear:
+ usb_set_intfdata(intf, NULL);
+ usb_put_dev(dev->udev);
+err_out_free:
+ kfree(dev);
+ return err;
+}
+
+static void ds_disconnect(struct usb_interface *intf)
+{
+ struct ds_device *dev;
+
+ dev = usb_get_intfdata(intf);
+ if (!dev)
+ return;
+
+ mutex_lock(&ds_mutex);
+ list_del(&dev->ds_entry);
+ mutex_unlock(&ds_mutex);
+
+ ds_w1_fini(dev);
+
+ usb_set_intfdata(intf, NULL);
+
+ usb_put_dev(dev->udev);
+ kfree(dev);
+}
+
+static int ds_init(void)
+{
+ int err;
+
+ err = usb_register(&ds_driver);
+ if (err) {
+ printk(KERN_INFO "Failed to register DS9490R USB device: err=%d.\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static void ds_fini(void)
+{
+ usb_deregister(&ds_driver);
+}
+
+module_init(ds_init);
+module_exit(ds_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
+MODULE_DESCRIPTION("DS2490 USB <-> W1 bus master driver (DS9490*)");
diff --git a/drivers/w1/masters/matrox_w1.c b/drivers/w1/masters/matrox_w1.c
new file mode 100644
index 0000000..1550431
--- /dev/null
+++ b/drivers/w1/masters/matrox_w1.c
@@ -0,0 +1,247 @@
+/*
+ * matrox_w1.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms 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 <asm/types.h>
+#include <asm/atomic.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/timer.h>
+#include <linux/slab.h>
+#include <linux/pci_ids.h>
+#include <linux/pci.h>
+
+#include "../w1.h"
+#include "../w1_int.h"
+#include "../w1_log.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
+MODULE_DESCRIPTION("Driver for transport(Dallas 1-wire prtocol) over VGA DDC(matrox gpio).");
+
+static struct pci_device_id matrox_w1_tbl[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G400) },
+ { },
+};
+MODULE_DEVICE_TABLE(pci, matrox_w1_tbl);
+
+static int __devinit matrox_w1_probe(struct pci_dev *, const struct pci_device_id *);
+static void __devexit matrox_w1_remove(struct pci_dev *);
+
+static struct pci_driver matrox_w1_pci_driver = {
+ .name = "matrox_w1",
+ .id_table = matrox_w1_tbl,
+ .probe = matrox_w1_probe,
+ .remove = __devexit_p(matrox_w1_remove),
+};
+
+/*
+ * Matrox G400 DDC registers.
+ */
+
+#define MATROX_G400_DDC_CLK (1<<4)
+#define MATROX_G400_DDC_DATA (1<<1)
+
+#define MATROX_BASE 0x3C00
+#define MATROX_STATUS 0x1e14
+
+#define MATROX_PORT_INDEX_OFFSET 0x00
+#define MATROX_PORT_DATA_OFFSET 0x0A
+
+#define MATROX_GET_CONTROL 0x2A
+#define MATROX_GET_DATA 0x2B
+#define MATROX_CURSOR_CTL 0x06
+
+struct matrox_device
+{
+ void __iomem *base_addr;
+ void __iomem *port_index;
+ void __iomem *port_data;
+ u8 data_mask;
+
+ unsigned long phys_addr;
+ void __iomem *virt_addr;
+ unsigned long found;
+
+ struct w1_bus_master *bus_master;
+};
+
+static u8 matrox_w1_read_ddc_bit(void *);
+static void matrox_w1_write_ddc_bit(void *, u8);
+
+/*
+ * These functions read and write DDC Data bit.
+ *
+ * Using tristate pins, since i can't find any open-drain pin in whole motherboard.
+ * Unfortunately we can't connect to Intel's 82801xx IO controller
+ * since we don't know motherboard schema, which has pretty unused(may be not) GPIO.
+ *
+ * I've heard that PIIX also has open drain pin.
+ *
+ * Port mapping.
+ */
+static __inline__ u8 matrox_w1_read_reg(struct matrox_device *dev, u8 reg)
+{
+ u8 ret;
+
+ writeb(reg, dev->port_index);
+ ret = readb(dev->port_data);
+ barrier();
+
+ return ret;
+}
+
+static __inline__ void matrox_w1_write_reg(struct matrox_device *dev, u8 reg, u8 val)
+{
+ writeb(reg, dev->port_index);
+ writeb(val, dev->port_data);
+ wmb();
+}
+
+static void matrox_w1_write_ddc_bit(void *data, u8 bit)
+{
+ u8 ret;
+ struct matrox_device *dev = data;
+
+ if (bit)
+ bit = 0;
+ else
+ bit = dev->data_mask;
+
+ ret = matrox_w1_read_reg(dev, MATROX_GET_CONTROL);
+ matrox_w1_write_reg(dev, MATROX_GET_CONTROL, ((ret & ~dev->data_mask) | bit));
+ matrox_w1_write_reg(dev, MATROX_GET_DATA, 0x00);
+}
+
+static u8 matrox_w1_read_ddc_bit(void *data)
+{
+ u8 ret;
+ struct matrox_device *dev = data;
+
+ ret = matrox_w1_read_reg(dev, MATROX_GET_DATA);
+
+ return ret;
+}
+
+static void matrox_w1_hw_init(struct matrox_device *dev)
+{
+ matrox_w1_write_reg(dev, MATROX_GET_DATA, 0xFF);
+ matrox_w1_write_reg(dev, MATROX_GET_CONTROL, 0x00);
+}
+
+static int __devinit matrox_w1_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct matrox_device *dev;
+ int err;
+
+ assert(pdev != NULL);
+ assert(ent != NULL);
+
+ if (pdev->vendor != PCI_VENDOR_ID_MATROX || pdev->device != PCI_DEVICE_ID_MATROX_G400)
+ return -ENODEV;
+
+ dev = kzalloc(sizeof(struct matrox_device) +
+ sizeof(struct w1_bus_master), GFP_KERNEL);
+ if (!dev) {
+ dev_err(&pdev->dev,
+ "%s: Failed to create new matrox_device object.\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+
+ dev->bus_master = (struct w1_bus_master *)(dev + 1);
+
+ /*
+ * True for G400, for some other we need resource 0, see drivers/video/matrox/matroxfb_base.c
+ */
+
+ dev->phys_addr = pci_resource_start(pdev, 1);
+
+ dev->virt_addr = ioremap_nocache(dev->phys_addr, 16384);
+ if (!dev->virt_addr) {
+ dev_err(&pdev->dev, "%s: failed to ioremap(0x%lx, %d).\n",
+ __func__, dev->phys_addr, 16384);
+ err = -EIO;
+ goto err_out_free_device;
+ }
+
+ dev->base_addr = dev->virt_addr + MATROX_BASE;
+ dev->port_index = dev->base_addr + MATROX_PORT_INDEX_OFFSET;
+ dev->port_data = dev->base_addr + MATROX_PORT_DATA_OFFSET;
+ dev->data_mask = (MATROX_G400_DDC_DATA);
+
+ matrox_w1_hw_init(dev);
+
+ dev->bus_master->data = dev;
+ dev->bus_master->read_bit = &matrox_w1_read_ddc_bit;
+ dev->bus_master->write_bit = &matrox_w1_write_ddc_bit;
+
+ err = w1_add_master_device(dev->bus_master);
+ if (err)
+ goto err_out_free_device;
+
+ pci_set_drvdata(pdev, dev);
+
+ dev->found = 1;
+
+ dev_info(&pdev->dev, "Matrox G400 GPIO transport layer for 1-wire.\n");
+
+ return 0;
+
+err_out_free_device:
+ if (dev->virt_addr)
+ iounmap(dev->virt_addr);
+ kfree(dev);
+
+ return err;
+}
+
+static void __devexit matrox_w1_remove(struct pci_dev *pdev)
+{
+ struct matrox_device *dev = pci_get_drvdata(pdev);
+
+ assert(dev != NULL);
+
+ if (dev->found) {
+ w1_remove_master_device(dev->bus_master);
+ iounmap(dev->virt_addr);
+ }
+ kfree(dev);
+}
+
+static int __init matrox_w1_init(void)
+{
+ return pci_register_driver(&matrox_w1_pci_driver);
+}
+
+static void __exit matrox_w1_fini(void)
+{
+ pci_unregister_driver(&matrox_w1_pci_driver);
+}
+
+module_init(matrox_w1_init);
+module_exit(matrox_w1_fini);
diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c
new file mode 100644
index 0000000..c973889
--- /dev/null
+++ b/drivers/w1/masters/omap_hdq.c
@@ -0,0 +1,725 @@
+/*
+ * drivers/w1/masters/omap_hdq.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/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <asm/irq.h>
+#include <mach/hardware.h>
+
+#include "../w1.h"
+#include "../w1_int.h"
+
+#define MOD_NAME "OMAP_HDQ:"
+
+#define OMAP_HDQ_REVISION 0x00
+#define OMAP_HDQ_TX_DATA 0x04
+#define OMAP_HDQ_RX_DATA 0x08
+#define OMAP_HDQ_CTRL_STATUS 0x0c
+#define OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK (1<<6)
+#define OMAP_HDQ_CTRL_STATUS_CLOCKENABLE (1<<5)
+#define OMAP_HDQ_CTRL_STATUS_GO (1<<4)
+#define OMAP_HDQ_CTRL_STATUS_INITIALIZATION (1<<2)
+#define OMAP_HDQ_CTRL_STATUS_DIR (1<<1)
+#define OMAP_HDQ_CTRL_STATUS_MODE (1<<0)
+#define OMAP_HDQ_INT_STATUS 0x10
+#define OMAP_HDQ_INT_STATUS_TXCOMPLETE (1<<2)
+#define OMAP_HDQ_INT_STATUS_RXCOMPLETE (1<<1)
+#define OMAP_HDQ_INT_STATUS_TIMEOUT (1<<0)
+#define OMAP_HDQ_SYSCONFIG 0x14
+#define OMAP_HDQ_SYSCONFIG_SOFTRESET (1<<1)
+#define OMAP_HDQ_SYSCONFIG_AUTOIDLE (1<<0)
+#define OMAP_HDQ_SYSSTATUS 0x18
+#define OMAP_HDQ_SYSSTATUS_RESETDONE (1<<0)
+
+#define OMAP_HDQ_FLAG_CLEAR 0
+#define OMAP_HDQ_FLAG_SET 1
+#define OMAP_HDQ_TIMEOUT (HZ/5)
+
+#define OMAP_HDQ_MAX_USER 4
+
+static DECLARE_WAIT_QUEUE_HEAD(hdq_wait_queue);
+static int w1_id;
+
+struct hdq_data {
+ struct device *dev;
+ void __iomem *hdq_base;
+ /* lock status update */
+ struct mutex hdq_mutex;
+ int hdq_usecount;
+ struct clk *hdq_ick;
+ struct clk *hdq_fck;
+ u8 hdq_irqstatus;
+ /* device lock */
+ spinlock_t hdq_spinlock;
+ /*
+ * Used to control the call to omap_hdq_get and omap_hdq_put.
+ * HDQ Protocol: Write the CMD|REG_address first, followed by
+ * the data wrire or read.
+ */
+ int init_trans;
+};
+
+static int __init omap_hdq_probe(struct platform_device *pdev);
+static int omap_hdq_remove(struct platform_device *pdev);
+
+static struct platform_driver omap_hdq_driver = {
+ .probe = omap_hdq_probe,
+ .remove = omap_hdq_remove,
+ .driver = {
+ .name = "omap_hdq",
+ },
+};
+
+static u8 omap_w1_read_byte(void *_hdq);
+static void omap_w1_write_byte(void *_hdq, u8 byte);
+static u8 omap_w1_reset_bus(void *_hdq);
+static void omap_w1_search_bus(void *_hdq, struct w1_master *master_dev,
+ u8 search_type, w1_slave_found_callback slave_found);
+
+
+static struct w1_bus_master omap_w1_master = {
+ .read_byte = omap_w1_read_byte,
+ .write_byte = omap_w1_write_byte,
+ .reset_bus = omap_w1_reset_bus,
+ .search = omap_w1_search_bus,
+};
+
+/* HDQ register I/O routines */
+static inline u8 hdq_reg_in(struct hdq_data *hdq_data, u32 offset)
+{
+ return __raw_readb(hdq_data->hdq_base + offset);
+}
+
+static inline void hdq_reg_out(struct hdq_data *hdq_data, u32 offset, u8 val)
+{
+ __raw_writeb(val, hdq_data->hdq_base + offset);
+}
+
+static inline u8 hdq_reg_merge(struct hdq_data *hdq_data, u32 offset,
+ u8 val, u8 mask)
+{
+ u8 new_val = (__raw_readb(hdq_data->hdq_base + offset) & ~mask)
+ | (val & mask);
+ __raw_writeb(new_val, hdq_data->hdq_base + offset);
+
+ return new_val;
+}
+
+/*
+ * Wait for one or more bits in flag change.
+ * HDQ_FLAG_SET: wait until any bit in the flag is set.
+ * HDQ_FLAG_CLEAR: wait until all bits in the flag are cleared.
+ * return 0 on success and -ETIMEDOUT in the case of timeout.
+ */
+static int hdq_wait_for_flag(struct hdq_data *hdq_data, u32 offset,
+ u8 flag, u8 flag_set, u8 *status)
+{
+ int ret = 0;
+ unsigned long timeout = jiffies + OMAP_HDQ_TIMEOUT;
+
+ if (flag_set == OMAP_HDQ_FLAG_CLEAR) {
+ /* wait for the flag clear */
+ while (((*status = hdq_reg_in(hdq_data, offset)) & flag)
+ && time_before(jiffies, timeout)) {
+ schedule_timeout_uninterruptible(1);
+ }
+ if (*status & flag)
+ ret = -ETIMEDOUT;
+ } else if (flag_set == OMAP_HDQ_FLAG_SET) {
+ /* wait for the flag set */
+ while (!((*status = hdq_reg_in(hdq_data, offset)) & flag)
+ && time_before(jiffies, timeout)) {
+ schedule_timeout_uninterruptible(1);
+ }
+ if (!(*status & flag))
+ ret = -ETIMEDOUT;
+ } else
+ return -EINVAL;
+
+ return ret;
+}
+
+/* write out a byte and fill *status with HDQ_INT_STATUS */
+static int hdq_write_byte(struct hdq_data *hdq_data, u8 val, u8 *status)
+{
+ int ret;
+ u8 tmp_status;
+ unsigned long irqflags;
+
+ *status = 0;
+
+ spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags);
+ /* clear interrupt flags via a dummy read */
+ hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS);
+ /* ISR loads it with new INT_STATUS */
+ hdq_data->hdq_irqstatus = 0;
+ spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags);
+
+ hdq_reg_out(hdq_data, OMAP_HDQ_TX_DATA, val);
+
+ /* set the GO bit */
+ hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS, OMAP_HDQ_CTRL_STATUS_GO,
+ OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO);
+ /* wait for the TXCOMPLETE bit */
+ ret = wait_event_timeout(hdq_wait_queue,
+ hdq_data->hdq_irqstatus, OMAP_HDQ_TIMEOUT);
+ if (ret == 0) {
+ dev_dbg(hdq_data->dev, "TX wait elapsed\n");
+ goto out;
+ }
+
+ *status = hdq_data->hdq_irqstatus;
+ /* check irqstatus */
+ if (!(*status & OMAP_HDQ_INT_STATUS_TXCOMPLETE)) {
+ dev_dbg(hdq_data->dev, "timeout waiting for"
+ "TXCOMPLETE/RXCOMPLETE, %x", *status);
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+
+ /* wait for the GO bit return to zero */
+ ret = hdq_wait_for_flag(hdq_data, OMAP_HDQ_CTRL_STATUS,
+ OMAP_HDQ_CTRL_STATUS_GO,
+ OMAP_HDQ_FLAG_CLEAR, &tmp_status);
+ if (ret) {
+ dev_dbg(hdq_data->dev, "timeout waiting GO bit"
+ "return to zero, %x", tmp_status);
+ }
+
+out:
+ return ret;
+}
+
+/* HDQ Interrupt service routine */
+static irqreturn_t hdq_isr(int irq, void *_hdq)
+{
+ struct hdq_data *hdq_data = _hdq;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags);
+ hdq_data->hdq_irqstatus = hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS);
+ spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags);
+ dev_dbg(hdq_data->dev, "hdq_isr: %x", hdq_data->hdq_irqstatus);
+
+ if (hdq_data->hdq_irqstatus &
+ (OMAP_HDQ_INT_STATUS_TXCOMPLETE | OMAP_HDQ_INT_STATUS_RXCOMPLETE
+ | OMAP_HDQ_INT_STATUS_TIMEOUT)) {
+ /* wake up sleeping process */
+ wake_up(&hdq_wait_queue);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/* HDQ Mode: always return success */
+static u8 omap_w1_reset_bus(void *_hdq)
+{
+ return 0;
+}
+
+/* W1 search callback function */
+static void omap_w1_search_bus(void *_hdq, struct w1_master *master_dev,
+ u8 search_type, w1_slave_found_callback slave_found)
+{
+ u64 module_id, rn_le, cs, id;
+
+ if (w1_id)
+ module_id = w1_id;
+ else
+ module_id = 0x1;
+
+ rn_le = cpu_to_le64(module_id);
+ /*
+ * HDQ might not obey truly the 1-wire spec.
+ * So calculate CRC based on module parameter.
+ */
+ cs = w1_calc_crc8((u8 *)&rn_le, 7);
+ id = (cs << 56) | module_id;
+
+ slave_found(master_dev, id);
+}
+
+static int _omap_hdq_reset(struct hdq_data *hdq_data)
+{
+ int ret;
+ u8 tmp_status;
+
+ hdq_reg_out(hdq_data, OMAP_HDQ_SYSCONFIG, OMAP_HDQ_SYSCONFIG_SOFTRESET);
+ /*
+ * Select HDQ mode & enable clocks.
+ * It is observed that INT flags can't be cleared via a read and GO/INIT
+ * won't return to zero if interrupt is disabled. So we always enable
+ * interrupt.
+ */
+ hdq_reg_out(hdq_data, OMAP_HDQ_CTRL_STATUS,
+ OMAP_HDQ_CTRL_STATUS_CLOCKENABLE |
+ OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK);
+
+ /* wait for reset to complete */
+ ret = hdq_wait_for_flag(hdq_data, OMAP_HDQ_SYSSTATUS,
+ OMAP_HDQ_SYSSTATUS_RESETDONE, OMAP_HDQ_FLAG_SET, &tmp_status);
+ if (ret)
+ dev_dbg(hdq_data->dev, "timeout waiting HDQ reset, %x",
+ tmp_status);
+ else {
+ hdq_reg_out(hdq_data, OMAP_HDQ_CTRL_STATUS,
+ OMAP_HDQ_CTRL_STATUS_CLOCKENABLE |
+ OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK);
+ hdq_reg_out(hdq_data, OMAP_HDQ_SYSCONFIG,
+ OMAP_HDQ_SYSCONFIG_AUTOIDLE);
+ }
+
+ return ret;
+}
+
+/* Issue break pulse to the device */
+static int omap_hdq_break(struct hdq_data *hdq_data)
+{
+ int ret = 0;
+ u8 tmp_status;
+ unsigned long irqflags;
+
+ ret = mutex_lock_interruptible(&hdq_data->hdq_mutex);
+ if (ret < 0) {
+ dev_dbg(hdq_data->dev, "Could not acquire mutex\n");
+ ret = -EINTR;
+ goto rtn;
+ }
+
+ spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags);
+ /* clear interrupt flags via a dummy read */
+ hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS);
+ /* ISR loads it with new INT_STATUS */
+ hdq_data->hdq_irqstatus = 0;
+ spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags);
+
+ /* set the INIT and GO bit */
+ hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS,
+ OMAP_HDQ_CTRL_STATUS_INITIALIZATION | OMAP_HDQ_CTRL_STATUS_GO,
+ OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_INITIALIZATION |
+ OMAP_HDQ_CTRL_STATUS_GO);
+
+ /* wait for the TIMEOUT bit */
+ ret = wait_event_timeout(hdq_wait_queue,
+ hdq_data->hdq_irqstatus, OMAP_HDQ_TIMEOUT);
+ if (ret == 0) {
+ dev_dbg(hdq_data->dev, "break wait elapsed\n");
+ ret = -EINTR;
+ goto out;
+ }
+
+ tmp_status = hdq_data->hdq_irqstatus;
+ /* check irqstatus */
+ if (!(tmp_status & OMAP_HDQ_INT_STATUS_TIMEOUT)) {
+ dev_dbg(hdq_data->dev, "timeout waiting for TIMEOUT, %x",
+ tmp_status);
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+ /*
+ * wait for both INIT and GO bits rerurn to zero.
+ * zero wait time expected for interrupt mode.
+ */
+ ret = hdq_wait_for_flag(hdq_data, OMAP_HDQ_CTRL_STATUS,
+ OMAP_HDQ_CTRL_STATUS_INITIALIZATION |
+ OMAP_HDQ_CTRL_STATUS_GO, OMAP_HDQ_FLAG_CLEAR,
+ &tmp_status);
+ if (ret)
+ dev_dbg(hdq_data->dev, "timeout waiting INIT&GO bits"
+ "return to zero, %x", tmp_status);
+
+out:
+ mutex_unlock(&hdq_data->hdq_mutex);
+rtn:
+ return ret;
+}
+
+static int hdq_read_byte(struct hdq_data *hdq_data, u8 *val)
+{
+ int ret = 0;
+ u8 status;
+ unsigned long timeout = jiffies + OMAP_HDQ_TIMEOUT;
+
+ ret = mutex_lock_interruptible(&hdq_data->hdq_mutex);
+ if (ret < 0) {
+ ret = -EINTR;
+ goto rtn;
+ }
+
+ if (!hdq_data->hdq_usecount) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!(hdq_data->hdq_irqstatus & OMAP_HDQ_INT_STATUS_RXCOMPLETE)) {
+ hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS,
+ OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO,
+ OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO);
+ /*
+ * The RX comes immediately after TX. It
+ * triggers another interrupt before we
+ * sleep. So we have to wait for RXCOMPLETE bit.
+ */
+ while (!(hdq_data->hdq_irqstatus
+ & OMAP_HDQ_INT_STATUS_RXCOMPLETE)
+ && time_before(jiffies, timeout)) {
+ schedule_timeout_uninterruptible(1);
+ }
+ hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS, 0,
+ OMAP_HDQ_CTRL_STATUS_DIR);
+ status = hdq_data->hdq_irqstatus;
+ /* check irqstatus */
+ if (!(status & OMAP_HDQ_INT_STATUS_RXCOMPLETE)) {
+ dev_dbg(hdq_data->dev, "timeout waiting for"
+ "RXCOMPLETE, %x", status);
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+ }
+ /* the data is ready. Read it in! */
+ *val = hdq_reg_in(hdq_data, OMAP_HDQ_RX_DATA);
+out:
+ mutex_unlock(&hdq_data->hdq_mutex);
+rtn:
+ return 0;
+
+}
+
+/* Enable clocks and set the controller to HDQ mode */
+static int omap_hdq_get(struct hdq_data *hdq_data)
+{
+ int ret = 0;
+
+ ret = mutex_lock_interruptible(&hdq_data->hdq_mutex);
+ if (ret < 0) {
+ ret = -EINTR;
+ goto rtn;
+ }
+
+ if (OMAP_HDQ_MAX_USER == hdq_data->hdq_usecount) {
+ dev_dbg(hdq_data->dev, "attempt to exceed the max use count");
+ ret = -EINVAL;
+ goto out;
+ } else {
+ hdq_data->hdq_usecount++;
+ try_module_get(THIS_MODULE);
+ if (1 == hdq_data->hdq_usecount) {
+ if (clk_enable(hdq_data->hdq_ick)) {
+ dev_dbg(hdq_data->dev, "Can not enable ick\n");
+ ret = -ENODEV;
+ goto clk_err;
+ }
+ if (clk_enable(hdq_data->hdq_fck)) {
+ dev_dbg(hdq_data->dev, "Can not enable fck\n");
+ clk_disable(hdq_data->hdq_ick);
+ ret = -ENODEV;
+ goto clk_err;
+ }
+
+ /* make sure HDQ is out of reset */
+ if (!(hdq_reg_in(hdq_data, OMAP_HDQ_SYSSTATUS) &
+ OMAP_HDQ_SYSSTATUS_RESETDONE)) {
+ ret = _omap_hdq_reset(hdq_data);
+ if (ret)
+ /* back up the count */
+ hdq_data->hdq_usecount--;
+ } else {
+ /* select HDQ mode & enable clocks */
+ hdq_reg_out(hdq_data, OMAP_HDQ_CTRL_STATUS,
+ OMAP_HDQ_CTRL_STATUS_CLOCKENABLE |
+ OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK);
+ hdq_reg_out(hdq_data, OMAP_HDQ_SYSCONFIG,
+ OMAP_HDQ_SYSCONFIG_AUTOIDLE);
+ hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS);
+ }
+ }
+ }
+
+clk_err:
+ clk_put(hdq_data->hdq_ick);
+ clk_put(hdq_data->hdq_fck);
+out:
+ mutex_unlock(&hdq_data->hdq_mutex);
+rtn:
+ return ret;
+}
+
+/* Disable clocks to the module */
+static int omap_hdq_put(struct hdq_data *hdq_data)
+{
+ int ret = 0;
+
+ ret = mutex_lock_interruptible(&hdq_data->hdq_mutex);
+ if (ret < 0)
+ return -EINTR;
+
+ if (0 == hdq_data->hdq_usecount) {
+ dev_dbg(hdq_data->dev, "attempt to decrement use count"
+ "when it is zero");
+ ret = -EINVAL;
+ } else {
+ hdq_data->hdq_usecount--;
+ module_put(THIS_MODULE);
+ if (0 == hdq_data->hdq_usecount) {
+ clk_disable(hdq_data->hdq_ick);
+ clk_disable(hdq_data->hdq_fck);
+ }
+ }
+ mutex_unlock(&hdq_data->hdq_mutex);
+
+ return ret;
+}
+
+/* Read a byte of data from the device */
+static u8 omap_w1_read_byte(void *_hdq)
+{
+ struct hdq_data *hdq_data = _hdq;
+ u8 val = 0;
+ int ret;
+
+ ret = hdq_read_byte(hdq_data, &val);
+ if (ret) {
+ ret = mutex_lock_interruptible(&hdq_data->hdq_mutex);
+ if (ret < 0) {
+ dev_dbg(hdq_data->dev, "Could not acquire mutex\n");
+ return -EINTR;
+ }
+ hdq_data->init_trans = 0;
+ mutex_unlock(&hdq_data->hdq_mutex);
+ omap_hdq_put(hdq_data);
+ return -1;
+ }
+
+ /* Write followed by a read, release the module */
+ if (hdq_data->init_trans) {
+ ret = mutex_lock_interruptible(&hdq_data->hdq_mutex);
+ if (ret < 0) {
+ dev_dbg(hdq_data->dev, "Could not acquire mutex\n");
+ return -EINTR;
+ }
+ hdq_data->init_trans = 0;
+ mutex_unlock(&hdq_data->hdq_mutex);
+ omap_hdq_put(hdq_data);
+ }
+
+ return val;
+}
+
+/* Write a byte of data to the device */
+static void omap_w1_write_byte(void *_hdq, u8 byte)
+{
+ struct hdq_data *hdq_data = _hdq;
+ int ret;
+ u8 status;
+
+ /* First write to initialize the transfer */
+ if (hdq_data->init_trans == 0)
+ omap_hdq_get(hdq_data);
+
+ ret = mutex_lock_interruptible(&hdq_data->hdq_mutex);
+ if (ret < 0) {
+ dev_dbg(hdq_data->dev, "Could not acquire mutex\n");
+ return;
+ }
+ hdq_data->init_trans++;
+ mutex_unlock(&hdq_data->hdq_mutex);
+
+ ret = hdq_write_byte(hdq_data, byte, &status);
+ if (ret == 0) {
+ dev_dbg(hdq_data->dev, "TX failure:Ctrl status %x\n", status);
+ return;
+ }
+
+ /* Second write, data transfered. Release the module */
+ if (hdq_data->init_trans > 1) {
+ omap_hdq_put(hdq_data);
+ ret = mutex_lock_interruptible(&hdq_data->hdq_mutex);
+ if (ret < 0) {
+ dev_dbg(hdq_data->dev, "Could not acquire mutex\n");
+ return;
+ }
+ hdq_data->init_trans = 0;
+ mutex_unlock(&hdq_data->hdq_mutex);
+ }
+
+ return;
+}
+
+static int __init omap_hdq_probe(struct platform_device *pdev)
+{
+ struct hdq_data *hdq_data;
+ struct resource *res;
+ int ret, irq;
+ u8 rev;
+
+ hdq_data = kmalloc(sizeof(*hdq_data), GFP_KERNEL);
+ if (!hdq_data) {
+ dev_dbg(&pdev->dev, "unable to allocate memory\n");
+ ret = -ENOMEM;
+ goto err_kmalloc;
+ }
+
+ hdq_data->dev = &pdev->dev;
+ platform_set_drvdata(pdev, hdq_data);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_dbg(&pdev->dev, "unable to get resource\n");
+ ret = -ENXIO;
+ goto err_resource;
+ }
+
+ hdq_data->hdq_base = ioremap(res->start, SZ_4K);
+ if (!hdq_data->hdq_base) {
+ dev_dbg(&pdev->dev, "ioremap failed\n");
+ ret = -EINVAL;
+ goto err_ioremap;
+ }
+
+ /* get interface & functional clock objects */
+ hdq_data->hdq_ick = clk_get(&pdev->dev, "hdq_ick");
+ hdq_data->hdq_fck = clk_get(&pdev->dev, "hdq_fck");
+
+ if (IS_ERR(hdq_data->hdq_ick) || IS_ERR(hdq_data->hdq_fck)) {
+ dev_dbg(&pdev->dev, "Can't get HDQ clock objects\n");
+ if (IS_ERR(hdq_data->hdq_ick)) {
+ ret = PTR_ERR(hdq_data->hdq_ick);
+ goto err_clk;
+ }
+ if (IS_ERR(hdq_data->hdq_fck)) {
+ ret = PTR_ERR(hdq_data->hdq_fck);
+ clk_put(hdq_data->hdq_ick);
+ goto err_clk;
+ }
+ }
+
+ hdq_data->hdq_usecount = 0;
+ mutex_init(&hdq_data->hdq_mutex);
+
+ if (clk_enable(hdq_data->hdq_ick)) {
+ dev_dbg(&pdev->dev, "Can not enable ick\n");
+ ret = -ENODEV;
+ goto err_intfclk;
+ }
+
+ if (clk_enable(hdq_data->hdq_fck)) {
+ dev_dbg(&pdev->dev, "Can not enable fck\n");
+ ret = -ENODEV;
+ goto err_fnclk;
+ }
+
+ rev = hdq_reg_in(hdq_data, OMAP_HDQ_REVISION);
+ dev_info(&pdev->dev, "OMAP HDQ Hardware Rev %c.%c. Driver in %s mode\n",
+ (rev >> 4) + '0', (rev & 0x0f) + '0', "Interrupt");
+
+ spin_lock_init(&hdq_data->hdq_spinlock);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ ret = -ENXIO;
+ goto err_irq;
+ }
+
+ ret = request_irq(irq, hdq_isr, IRQF_DISABLED, "omap_hdq", hdq_data);
+ if (ret < 0) {
+ dev_dbg(&pdev->dev, "could not request irq\n");
+ goto err_irq;
+ }
+
+ omap_hdq_break(hdq_data);
+
+ /* don't clock the HDQ until it is needed */
+ clk_disable(hdq_data->hdq_ick);
+ clk_disable(hdq_data->hdq_fck);
+
+ omap_w1_master.data = hdq_data;
+
+ ret = w1_add_master_device(&omap_w1_master);
+ if (ret) {
+ dev_dbg(&pdev->dev, "Failure in registering w1 master\n");
+ goto err_w1;
+ }
+
+ return 0;
+
+err_w1:
+err_irq:
+ clk_disable(hdq_data->hdq_fck);
+
+err_fnclk:
+ clk_disable(hdq_data->hdq_ick);
+
+err_intfclk:
+ clk_put(hdq_data->hdq_ick);
+ clk_put(hdq_data->hdq_fck);
+
+err_clk:
+ iounmap(hdq_data->hdq_base);
+
+err_ioremap:
+err_resource:
+ platform_set_drvdata(pdev, NULL);
+ kfree(hdq_data);
+
+err_kmalloc:
+ return ret;
+
+}
+
+static int omap_hdq_remove(struct platform_device *pdev)
+{
+ struct hdq_data *hdq_data = platform_get_drvdata(pdev);
+
+ mutex_lock(&hdq_data->hdq_mutex);
+
+ if (hdq_data->hdq_usecount) {
+ dev_dbg(&pdev->dev, "removed when use count is not zero\n");
+ return -EBUSY;
+ }
+
+ mutex_unlock(&hdq_data->hdq_mutex);
+
+ /* remove module dependency */
+ clk_put(hdq_data->hdq_ick);
+ clk_put(hdq_data->hdq_fck);
+ free_irq(INT_24XX_HDQ_IRQ, hdq_data);
+ platform_set_drvdata(pdev, NULL);
+ iounmap(hdq_data->hdq_base);
+ kfree(hdq_data);
+
+ return 0;
+}
+
+static int __init
+omap_hdq_init(void)
+{
+ return platform_driver_register(&omap_hdq_driver);
+}
+module_init(omap_hdq_init);
+
+static void __exit
+omap_hdq_exit(void)
+{
+ platform_driver_unregister(&omap_hdq_driver);
+}
+module_exit(omap_hdq_exit);
+
+module_param(w1_id, int, S_IRUSR);
+MODULE_PARM_DESC(w1_id, "1-wire id for the slave detection");
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("HDQ driver Library");
+MODULE_LICENSE("GPL");
diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c
new file mode 100644
index 0000000..9e1138a
--- /dev/null
+++ b/drivers/w1/masters/w1-gpio.c
@@ -0,0 +1,124 @@
+/*
+ * w1-gpio - GPIO w1 bus master driver
+ *
+ * Copyright (C) 2007 Ville Syrjala <syrjala@sci.fi>
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/w1-gpio.h>
+
+#include "../w1.h"
+#include "../w1_int.h"
+
+#include <asm/gpio.h>
+
+static void w1_gpio_write_bit_dir(void *data, u8 bit)
+{
+ struct w1_gpio_platform_data *pdata = data;
+
+ if (bit)
+ gpio_direction_input(pdata->pin);
+ else
+ gpio_direction_output(pdata->pin, 0);
+}
+
+static void w1_gpio_write_bit_val(void *data, u8 bit)
+{
+ struct w1_gpio_platform_data *pdata = data;
+
+ gpio_set_value(pdata->pin, bit);
+}
+
+static u8 w1_gpio_read_bit(void *data)
+{
+ struct w1_gpio_platform_data *pdata = data;
+
+ return gpio_get_value(pdata->pin);
+}
+
+static int __init w1_gpio_probe(struct platform_device *pdev)
+{
+ struct w1_bus_master *master;
+ struct w1_gpio_platform_data *pdata = pdev->dev.platform_data;
+ int err;
+
+ if (!pdata)
+ return -ENXIO;
+
+ master = kzalloc(sizeof(struct w1_bus_master), GFP_KERNEL);
+ if (!master)
+ return -ENOMEM;
+
+ err = gpio_request(pdata->pin, "w1");
+ if (err)
+ goto free_master;
+
+ master->data = pdata;
+ master->read_bit = w1_gpio_read_bit;
+
+ if (pdata->is_open_drain) {
+ gpio_direction_output(pdata->pin, 1);
+ master->write_bit = w1_gpio_write_bit_val;
+ } else {
+ gpio_direction_input(pdata->pin);
+ master->write_bit = w1_gpio_write_bit_dir;
+ }
+
+ err = w1_add_master_device(master);
+ if (err)
+ goto free_gpio;
+
+ platform_set_drvdata(pdev, master);
+
+ return 0;
+
+ free_gpio:
+ gpio_free(pdata->pin);
+ free_master:
+ kfree(master);
+
+ return err;
+}
+
+static int __exit w1_gpio_remove(struct platform_device *pdev)
+{
+ struct w1_bus_master *master = platform_get_drvdata(pdev);
+ struct w1_gpio_platform_data *pdata = pdev->dev.platform_data;
+
+ w1_remove_master_device(master);
+ gpio_free(pdata->pin);
+ kfree(master);
+
+ return 0;
+}
+
+static struct platform_driver w1_gpio_driver = {
+ .driver = {
+ .name = "w1-gpio",
+ .owner = THIS_MODULE,
+ },
+ .remove = __exit_p(w1_gpio_remove),
+};
+
+static int __init w1_gpio_init(void)
+{
+ return platform_driver_probe(&w1_gpio_driver, w1_gpio_probe);
+}
+
+static void __exit w1_gpio_exit(void)
+{
+ platform_driver_unregister(&w1_gpio_driver);
+}
+
+module_init(w1_gpio_init);
+module_exit(w1_gpio_exit);
+
+MODULE_DESCRIPTION("GPIO w1 bus master driver");
+MODULE_AUTHOR("Ville Syrjala <syrjala@sci.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig
new file mode 100644
index 0000000..8d0b1fb
--- /dev/null
+++ b/drivers/w1/slaves/Kconfig
@@ -0,0 +1,54 @@
+#
+# 1-wire slaves configuration
+#
+
+menu "1-wire Slaves"
+
+config W1_SLAVE_THERM
+ tristate "Thermal family implementation"
+ help
+ Say Y here if you want to connect 1-wire thermal sensors to your
+ wire.
+
+config W1_SLAVE_SMEM
+ tristate "Simple 64bit memory family implementation"
+ help
+ Say Y here if you want to connect 1-wire
+ simple 64bit memory rom(ds2401/ds2411/ds1990*) to your wire.
+
+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_DS2433_CRC
+ bool "Protect DS2433 data with a CRC16"
+ depends on W1_SLAVE_DS2433
+ select CRC16
+ help
+ Say Y here to protect DS2433 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_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.
+
+ 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_BQ27000
+ tristate "BQ27000 slave support"
+ depends on W1
+ 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
new file mode 100644
index 0000000..990f400
--- /dev/null
+++ b/drivers/w1/slaves/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the Dallas's 1-wire slaves.
+#
+
+obj-$(CONFIG_W1_SLAVE_THERM) += w1_therm.o
+obj-$(CONFIG_W1_SLAVE_SMEM) += w1_smem.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
diff --git a/drivers/w1/slaves/w1_bq27000.c b/drivers/w1/slaves/w1_bq27000.c
new file mode 100644
index 0000000..8f4c91f
--- /dev/null
+++ b/drivers/w1/slaves/w1_bq27000.c
@@ -0,0 +1,123 @@
+/*
+ * 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 "../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;
+
+void w1_bq27000_write(struct device *dev, u8 buf, u8 reg)
+{
+ struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+
+ if (!dev) {
+ pr_info("Could not obtain slave dev ptr\n");
+ return;
+ }
+
+ w1_write_8(sl->master, HDQ_CMD_WRITE | reg);
+ w1_write_8(sl->master, buf);
+}
+EXPORT_SYMBOL(w1_bq27000_write);
+
+int w1_bq27000_read(struct device *dev, u8 reg)
+{
+ u8 val;
+ struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+
+ if (!dev)
+ return 0;
+
+ w1_write_8(sl->master, HDQ_CMD_READ | reg);
+ val = w1_read_8(sl->master);
+
+ return val;
+}
+EXPORT_SYMBOL(w1_bq27000_read);
+
+static int w1_bq27000_add_slave(struct w1_slave *sl)
+{
+ int ret;
+ int id = 1;
+ struct platform_device *pdev;
+
+ pdev = platform_device_alloc("bq27000-battery", id);
+ if (!pdev) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ 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_unregister(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_ds2431.c b/drivers/w1/slaves/w1_ds2431.c
new file mode 100644
index 0000000..2c6c0cf
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2431.c
@@ -0,0 +1,312 @@
+/*
+ * 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 w1_f2d_read_bin(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->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->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 w1_f2d_write_bin(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->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->mutex);
+
+ return count;
+}
+
+static struct bin_attribute w1_f2d_bin_attr = {
+ .attr = {
+ .name = "eeprom",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .size = W1_F2D_EEPROM_SIZE,
+ .read = w1_f2d_read_bin,
+ .write = w1_f2d_write_bin,
+};
+
+static int w1_f2d_add_slave(struct w1_slave *sl)
+{
+ return sysfs_create_bin_file(&sl->dev.kobj, &w1_f2d_bin_attr);
+}
+
+static void w1_f2d_remove_slave(struct w1_slave *sl)
+{
+ sysfs_remove_bin_file(&sl->dev.kobj, &w1_f2d_bin_attr);
+}
+
+static struct w1_family_ops w1_f2d_fops = {
+ .add_slave = w1_f2d_add_slave,
+ .remove_slave = w1_f2d_remove_slave,
+};
+
+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");
diff --git a/drivers/w1/slaves/w1_ds2433.c b/drivers/w1/slaves/w1_ds2433.c
new file mode 100644
index 0000000..858c16a
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2433.c
@@ -0,0 +1,316 @@
+/*
+ * w1_ds2433.c - w1 family 23 (DS2433) driver
+ *
+ * Copyright (c) 2005 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>
+#ifdef CONFIG_W1_SLAVE_DS2433_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"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>");
+MODULE_DESCRIPTION("w1 family 23 driver for DS2433, 4kb EEPROM");
+
+#define W1_EEPROM_SIZE 512
+#define W1_PAGE_COUNT 16
+#define W1_PAGE_SIZE 32
+#define W1_PAGE_BITS 5
+#define W1_PAGE_MASK 0x1F
+
+#define W1_F23_TIME 300
+
+#define W1_F23_READ_EEPROM 0xF0
+#define W1_F23_WRITE_SCRATCH 0x0F
+#define W1_F23_READ_SCRATCH 0xAA
+#define W1_F23_COPY_SCRATCH 0x55
+
+struct w1_f23_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_f23_fix_count(loff_t off, size_t count, size_t size)
+{
+ if (off > size)
+ return 0;
+
+ if ((off + count) > size)
+ return (size - off);
+
+ return count;
+}
+
+#ifdef CONFIG_W1_SLAVE_DS2433_CRC
+static int w1_f23_refresh_block(struct w1_slave *sl, struct w1_f23_data *data,
+ int block)
+{
+ u8 wrbuf[3];
+ int off = block * W1_PAGE_SIZE;
+
+ if (data->validcrc & (1 << block))
+ return 0;
+
+ if (w1_reset_select_slave(sl)) {
+ data->validcrc = 0;
+ return -EIO;
+ }
+
+ wrbuf[0] = W1_F23_READ_EEPROM;
+ wrbuf[1] = off & 0xff;
+ wrbuf[2] = off >> 8;
+ w1_write_block(sl->master, wrbuf, 3);
+ w1_read_block(sl->master, &data->memory[off], W1_PAGE_SIZE);
+
+ /* cache the block if the CRC is valid */
+ if (crc16(CRC16_INIT, &data->memory[off], W1_PAGE_SIZE) == CRC16_VALID)
+ data->validcrc |= (1 << block);
+
+ return 0;
+}
+#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)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+#ifdef CONFIG_W1_SLAVE_DS2433_CRC
+ struct w1_f23_data *data = sl->family_data;
+ int i, min_page, max_page;
+#else
+ u8 wrbuf[3];
+#endif
+
+ if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0)
+ return 0;
+
+ mutex_lock(&sl->master->mutex);
+
+#ifdef CONFIG_W1_SLAVE_DS2433_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_f23_refresh_block(sl, data, i)) {
+ count = -EIO;
+ goto out_up;
+ }
+ }
+ memcpy(buf, &data->memory[off], count);
+
+#else /* CONFIG_W1_SLAVE_DS2433_CRC */
+
+ /* read directly from the EEPROM */
+ if (w1_reset_select_slave(sl)) {
+ count = -EIO;
+ goto out_up;
+ }
+
+ wrbuf[0] = W1_F23_READ_EEPROM;
+ wrbuf[1] = off & 0xff;
+ wrbuf[2] = off >> 8;
+ w1_write_block(sl->master, wrbuf, 3);
+ w1_read_block(sl->master, buf, count);
+
+#endif /* CONFIG_W1_SLAVE_DS2433_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_f23_write(struct w1_slave *sl, int addr, int len, const u8 *data)
+{
+ u8 wrbuf[4];
+ u8 rdbuf[W1_PAGE_SIZE + 3];
+ u8 es = (addr + len - 1) & 0x1f;
+
+ /* Write the data to the scratchpad */
+ if (w1_reset_select_slave(sl))
+ return -1;
+
+ wrbuf[0] = W1_F23_WRITE_SCRATCH;
+ wrbuf[1] = addr & 0xff;
+ wrbuf[2] = addr >> 8;
+
+ w1_write_block(sl->master, wrbuf, 3);
+ w1_write_block(sl->master, data, len);
+
+ /* Read the scratchpad and verify */
+ if (w1_reset_select_slave(sl))
+ return -1;
+
+ w1_write_8(sl->master, W1_F23_READ_SCRATCH);
+ w1_read_block(sl->master, rdbuf, len + 3);
+
+ /* Compare what was read against the data written */
+ if ((rdbuf[0] != wrbuf[1]) || (rdbuf[1] != wrbuf[2]) ||
+ (rdbuf[2] != es) || (memcmp(data, &rdbuf[3], len) != 0))
+ return -1;
+
+ /* Copy the scratchpad to EEPROM */
+ if (w1_reset_select_slave(sl))
+ return -1;
+
+ wrbuf[0] = W1_F23_COPY_SCRATCH;
+ wrbuf[3] = es;
+ w1_write_block(sl->master, wrbuf, 4);
+
+ /* Sleep for 5 ms to wait for the write to complete */
+ msleep(5);
+
+ /* Reset the bus to wake up the EEPROM (this may not be needed) */
+ w1_reset_bus(sl->master);
+
+ return 0;
+}
+
+static ssize_t w1_f23_write_bin(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;
+
+ if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0)
+ return 0;
+
+#ifdef CONFIG_W1_SLAVE_DS2433_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_SLAVE_DS2433_CRC */
+
+ 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_f23_write(sl, addr, len, &buf[idx]) < 0) {
+ count = -EIO;
+ goto out_up;
+ }
+ idx += len;
+ }
+
+out_up:
+ mutex_unlock(&sl->master->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 int w1_f23_add_slave(struct w1_slave *sl)
+{
+ int err;
+#ifdef CONFIG_W1_SLAVE_DS2433_CRC
+ struct w1_f23_data *data;
+
+ data = kzalloc(sizeof(struct w1_f23_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ 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;
+}
+
+static void w1_f23_remove_slave(struct w1_slave *sl)
+{
+#ifdef CONFIG_W1_SLAVE_DS2433_CRC
+ 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,
+};
+
+static struct w1_family w1_family_23 = {
+ .fid = W1_EEPROM_DS2433,
+ .fops = &w1_f23_fops,
+};
+
+static int __init w1_f23_init(void)
+{
+ return w1_register_family(&w1_family_23);
+}
+
+static void __exit w1_f23_fini(void)
+{
+ w1_unregister_family(&w1_family_23);
+}
+
+module_init(w1_f23_init);
+module_exit(w1_f23_fini);
diff --git a/drivers/w1/slaves/w1_ds2760.c b/drivers/w1/slaves/w1_ds2760.c
new file mode 100644
index 0000000..1f09d4e
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2760.c
@@ -0,0 +1,209 @@
+/*
+ * 1-Wire implementation for the ds2760 chip
+ *
+ * Copyright © 2004-2005, Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>
+ *
+ * Use consistent with the GNU GPL is permitted,
+ * provided that this copyright notice is
+ * preserved in its entirety in all copies and derived works.
+ *
+ */
+
+#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_ds2760.h"
+
+static int w1_ds2760_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 (!dev)
+ return 0;
+
+ mutex_lock(&sl->master->mutex);
+
+ if (addr > DS2760_DATA_SIZE || addr < 0) {
+ count = 0;
+ goto out;
+ }
+ if (addr + count > DS2760_DATA_SIZE)
+ count = DS2760_DATA_SIZE - addr;
+
+ if (!w1_reset_select_slave(sl)) {
+ if (!io) {
+ w1_write_8(sl->master, W1_DS2760_READ_DATA);
+ w1_write_8(sl->master, addr);
+ count = w1_read_block(sl->master, buf, count);
+ } else {
+ w1_write_8(sl->master, W1_DS2760_WRITE_DATA);
+ w1_write_8(sl->master, addr);
+ w1_write_block(sl->master, buf, count);
+ /* XXX w1_write_block returns void, not n_written */
+ }
+ }
+
+out:
+ mutex_unlock(&sl->master->mutex);
+
+ return count;
+}
+
+int w1_ds2760_read(struct device *dev, char *buf, int addr, size_t count)
+{
+ return w1_ds2760_io(dev, buf, addr, count, 0);
+}
+
+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)
+{
+ 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,
+ },
+ .size = DS2760_DATA_SIZE,
+ .read = w1_ds2760_read_bin,
+};
+
+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 w1_ds2760_add_slave(struct w1_slave *sl)
+{
+ int ret;
+ int id;
+ struct platform_device *pdev;
+
+ id = new_bat_id();
+ if (id < 0) {
+ ret = id;
+ goto noid;
+ }
+
+ pdev = platform_device_alloc("ds2760-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;
+
+ 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);
+pdev_alloc_failed:
+ release_bat_id(id);
+noid:
+success:
+ return ret;
+}
+
+static void w1_ds2760_remove_slave(struct w1_slave *sl)
+{
+ struct platform_device *pdev = dev_get_drvdata(&sl->dev);
+ int id = pdev->id;
+
+ platform_device_unregister(pdev);
+ release_bat_id(id);
+ sysfs_remove_bin_file(&sl->dev.kobj, &w1_ds2760_bin_attr);
+}
+
+static struct w1_family_ops w1_ds2760_fops = {
+ .add_slave = w1_ds2760_add_slave,
+ .remove_slave = w1_ds2760_remove_slave,
+};
+
+static struct w1_family w1_ds2760_family = {
+ .fid = W1_FAMILY_DS2760,
+ .fops = &w1_ds2760_fops,
+};
+
+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);
+ return w1_register_family(&w1_ds2760_family);
+}
+
+static void __exit w1_ds2760_exit(void)
+{
+ w1_unregister_family(&w1_ds2760_family);
+ idr_destroy(&bat_idr);
+}
+
+EXPORT_SYMBOL(w1_ds2760_read);
+EXPORT_SYMBOL(w1_ds2760_write);
+
+module_init(w1_ds2760_init);
+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");
diff --git a/drivers/w1/slaves/w1_ds2760.h b/drivers/w1/slaves/w1_ds2760.h
new file mode 100644
index 0000000..f130242
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2760.h
@@ -0,0 +1,50 @@
+/*
+ * 1-Wire implementation for the ds2760 chip
+ *
+ * Copyright © 2004-2005, Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>
+ *
+ * Use consistent with the GNU GPL is permitted,
+ * provided that this copyright notice is
+ * preserved in its entirety in all copies and derived works.
+ *
+ */
+
+#ifndef __w1_ds2760_h__
+#define __w1_ds2760_h__
+
+/* Known commands to the DS2760 chip */
+#define W1_DS2760_SWAP 0xAA
+#define W1_DS2760_READ_DATA 0x69
+#define W1_DS2760_WRITE_DATA 0x6C
+#define W1_DS2760_COPY_DATA 0x48
+#define W1_DS2760_RECALL_DATA 0xB8
+#define W1_DS2760_LOCK 0x6A
+
+/* Number of valid register addresses */
+#define DS2760_DATA_SIZE 0x40
+
+#define DS2760_PROTECTION_REG 0x00
+#define DS2760_STATUS_REG 0x01
+#define DS2760_EEPROM_REG 0x07
+#define DS2760_SPECIAL_FEATURE_REG 0x08
+#define DS2760_VOLTAGE_MSB 0x0c
+#define DS2760_VOLTAGE_LSB 0x0d
+#define DS2760_CURRENT_MSB 0x0e
+#define DS2760_CURRENT_LSB 0x0f
+#define DS2760_CURRENT_ACCUM_MSB 0x10
+#define DS2760_CURRENT_ACCUM_LSB 0x11
+#define DS2760_TEMP_MSB 0x18
+#define DS2760_TEMP_LSB 0x19
+#define DS2760_EEPROM_BLOCK0 0x20
+#define DS2760_ACTIVE_FULL 0x20
+#define DS2760_EEPROM_BLOCK1 0x30
+#define DS2760_RATED_CAPACITY 0x32
+#define DS2760_CURRENT_OFFSET_BIAS 0x33
+#define DS2760_ACTIVE_EMPTY 0x3b
+
+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);
+
+#endif /* !__w1_ds2760_h__ */
diff --git a/drivers/w1/slaves/w1_smem.c b/drivers/w1/slaves/w1_smem.c
new file mode 100644
index 0000000..cc8c02e
--- /dev/null
+++ b/drivers/w1/slaves/w1_smem.c
@@ -0,0 +1,70 @@
+/*
+ * w1_smem.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the smems 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 <asm/types.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/types.h>
+
+#include "../w1.h"
+#include "../w1_int.h"
+#include "../w1_family.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
+MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, 64bit memory family.");
+
+static struct w1_family w1_smem_family_01 = {
+ .fid = W1_FAMILY_SMEM_01,
+};
+
+static struct w1_family w1_smem_family_81 = {
+ .fid = W1_FAMILY_SMEM_81,
+};
+
+static int __init w1_smem_init(void)
+{
+ int err;
+
+ err = w1_register_family(&w1_smem_family_01);
+ if (err)
+ return err;
+
+ err = w1_register_family(&w1_smem_family_81);
+ if (err) {
+ w1_unregister_family(&w1_smem_family_01);
+ return err;
+ }
+
+ return 0;
+}
+
+static void __exit w1_smem_fini(void)
+{
+ w1_unregister_family(&w1_smem_family_01);
+ w1_unregister_family(&w1_smem_family_81);
+}
+
+module_init(w1_smem_init);
+module_exit(w1_smem_fini);
diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c
new file mode 100644
index 0000000..1ed3d55
--- /dev/null
+++ b/drivers/w1/slaves/w1_therm.c
@@ -0,0 +1,255 @@
+/*
+ * w1_therm.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * 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 <asm/types.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+
+#include "../w1.h"
+#include "../w1_int.h"
+#include "../w1_family.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
+MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, temperature family.");
+
+/* 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.
+ */
+static int w1_strong_pullup = 1;
+module_param_named(strong_pullup, w1_strong_pullup, int, 0);
+
+static u8 bad_roms[][9] = {
+ {0xaa, 0x00, 0x4b, 0x46, 0xff, 0xff, 0x0c, 0x10, 0x87},
+ {}
+ };
+
+static ssize_t w1_therm_read(struct device *device,
+ struct device_attribute *attr, char *buf);
+
+static struct device_attribute w1_therm_attr =
+ __ATTR(w1_slave, S_IRUGO, w1_therm_read, NULL);
+
+static int w1_therm_add_slave(struct w1_slave *sl)
+{
+ return device_create_file(&sl->dev, &w1_therm_attr);
+}
+
+static void w1_therm_remove_slave(struct w1_slave *sl)
+{
+ device_remove_file(&sl->dev, &w1_therm_attr);
+}
+
+static struct w1_family_ops w1_therm_fops = {
+ .add_slave = w1_therm_add_slave,
+ .remove_slave = w1_therm_remove_slave,
+};
+
+static struct w1_family w1_therm_family_DS18S20 = {
+ .fid = W1_THERM_DS18S20,
+ .fops = &w1_therm_fops,
+};
+
+static struct w1_family w1_therm_family_DS18B20 = {
+ .fid = W1_THERM_DS18B20,
+ .fops = &w1_therm_fops,
+};
+
+static struct w1_family w1_therm_family_DS1822 = {
+ .fid = W1_THERM_DS1822,
+ .fops = &w1_therm_fops,
+};
+
+struct w1_therm_family_converter
+{
+ u8 broken;
+ u16 reserved;
+ struct w1_family *f;
+ int (*convert)(u8 rom[9]);
+};
+
+/* The return value is millidegrees Centigrade. */
+static inline int w1_DS18B20_convert_temp(u8 rom[9]);
+static inline int w1_DS18S20_convert_temp(u8 rom[9]);
+
+static struct w1_therm_family_converter w1_therm_families[] = {
+ {
+ .f = &w1_therm_family_DS18S20,
+ .convert = w1_DS18S20_convert_temp
+ },
+ {
+ .f = &w1_therm_family_DS1822,
+ .convert = w1_DS18B20_convert_temp
+ },
+ {
+ .f = &w1_therm_family_DS18B20,
+ .convert = w1_DS18B20_convert_temp
+ },
+};
+
+static inline int w1_DS18B20_convert_temp(u8 rom[9])
+{
+ int t = ((s16)rom[1] << 8) | rom[0];
+ t = t*1000/16;
+ return t;
+}
+
+static inline int w1_DS18S20_convert_temp(u8 rom[9])
+{
+ int t, h;
+
+ if (!rom[7])
+ return 0;
+
+ if (rom[1] == 0)
+ t = ((s32)rom[0] >> 1)*1000;
+ else
+ t = 1000*(-1*(s32)(0x100-rom[0]) >> 1);
+
+ t -= 250;
+ h = 1000*((s32)rom[7] - (s32)rom[6]);
+ h /= (s32)rom[7];
+ t += h;
+
+ return t;
+}
+
+static inline int w1_convert_temp(u8 rom[9], u8 fid)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i)
+ if (w1_therm_families[i].f->fid == fid)
+ return w1_therm_families[i].convert(rom);
+
+ 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(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct w1_slave *sl = dev_to_w1_slave(device);
+ struct w1_master *dev = sl->master;
+ u8 rom[9], crc, verdict;
+ int i, max_trying = 10;
+ ssize_t c = PAGE_SIZE;
+
+ mutex_lock(&dev->mutex);
+
+ memset(rom, 0, sizeof(rom));
+
+ verdict = 0;
+ crc = 0;
+
+ while (max_trying--) {
+ if (!w1_reset_select_slave(sl)) {
+ int count = 0;
+ unsigned int tm = 750;
+
+ /* 750ms strong pullup (or delay) after the convert */
+ if (w1_strong_pullup)
+ w1_next_pullup(dev, tm);
+ w1_write_8(dev, W1_CONVERT_TEMP);
+ if (!w1_strong_pullup)
+ msleep(tm);
+
+ if (!w1_reset_select_slave(sl)) {
+
+ w1_write_8(dev, W1_READ_SCRATCHPAD);
+ if ((count = w1_read_block(dev, rom, 9)) != 9) {
+ dev_warn(device, "w1_read_block() "
+ "returned %u instead of 9.\n",
+ count);
+ }
+
+ crc = w1_calc_crc8(rom, 8);
+
+ if (rom[8] == crc)
+ verdict = 1;
+ }
+ }
+
+ if (!w1_therm_check_rom(rom))
+ break;
+ }
+
+ for (i = 0; i < 9; ++i)
+ 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));
+ else
+ dev_warn(device, "18S20 doesn't respond to CONVERT_TEMP.\n");
+
+ for (i = 0; i < 9; ++i)
+ c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", sl->rom[i]);
+
+ c -= snprintf(buf + PAGE_SIZE - c, c, "t=%d\n",
+ w1_convert_temp(rom, sl->family->fid));
+ mutex_unlock(&dev->mutex);
+
+ return PAGE_SIZE - c;
+}
+
+static int __init w1_therm_init(void)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i) {
+ err = w1_register_family(w1_therm_families[i].f);
+ if (err)
+ w1_therm_families[i].broken = 1;
+ }
+
+ return 0;
+}
+
+static void __exit w1_therm_fini(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i)
+ if (!w1_therm_families[i].broken)
+ w1_unregister_family(w1_therm_families[i].f);
+}
+
+module_init(w1_therm_init);
+module_exit(w1_therm_fini);
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c
new file mode 100644
index 0000000..3b615d4
--- /dev/null
+++ b/drivers/w1/w1.c
@@ -0,0 +1,1054 @@
+/*
+ * w1.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms 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/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+
+#include <asm/atomic.h>
+
+#include "w1.h"
+#include "w1_log.h"
+#include "w1_int.h"
+#include "w1_family.h"
+#include "w1_netlink.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
+MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol.");
+
+static int w1_timeout = 10;
+int w1_max_slave_count = 10;
+int w1_max_slave_ttl = 10;
+
+module_param_named(timeout, w1_timeout, int, 0);
+module_param_named(max_slave_count, w1_max_slave_count, int, 0);
+module_param_named(slave_ttl, w1_max_slave_ttl, int, 0);
+
+DEFINE_MUTEX(w1_mlock);
+LIST_HEAD(w1_masters);
+
+static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn);
+
+static int w1_master_match(struct device *dev, struct device_driver *drv)
+{
+ return 1;
+}
+
+static int w1_master_probe(struct device *dev)
+{
+ return -ENODEV;
+}
+
+static void w1_master_release(struct device *dev)
+{
+ struct w1_master *md = dev_to_w1_master(dev);
+
+ dev_dbg(dev, "%s: Releasing %s.\n", __func__, md->name);
+ memset(md, 0, sizeof(struct w1_master) + sizeof(struct w1_bus_master));
+ kfree(md);
+}
+
+static void w1_slave_release(struct device *dev)
+{
+ struct w1_slave *sl = dev_to_w1_slave(dev);
+
+ dev_dbg(dev, "%s: Releasing %s.\n", __func__, sl->name);
+
+ while (atomic_read(&sl->refcnt)) {
+ dev_dbg(dev, "Waiting for %s to become free: refcnt=%d.\n",
+ sl->name, atomic_read(&sl->refcnt));
+ if (msleep_interruptible(1000))
+ flush_signals(current);
+ }
+
+ w1_family_put(sl->family);
+ sl->master->slave_count--;
+
+ complete(&sl->released);
+}
+
+static ssize_t w1_slave_read_name(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w1_slave *sl = dev_to_w1_slave(dev);
+
+ return sprintf(buf, "%s\n", sl->name);
+}
+
+static ssize_t w1_slave_read_id(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct w1_slave *sl = dev_to_w1_slave(dev);
+ ssize_t count = sizeof(sl->reg_num);
+
+ memcpy(buf, (u8 *)&sl->reg_num, count);
+ return count;
+}
+
+static struct device_attribute w1_slave_attr_name =
+ __ATTR(name, S_IRUGO, w1_slave_read_name, NULL);
+static struct device_attribute w1_slave_attr_id =
+ __ATTR(id, S_IRUGO, w1_slave_read_id, NULL);
+
+/* Default family */
+
+static ssize_t w1_default_write(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);
+
+ mutex_lock(&sl->master->mutex);
+ if (w1_reset_select_slave(sl)) {
+ count = 0;
+ goto out_up;
+ }
+
+ w1_write_block(sl->master, buf, count);
+
+out_up:
+ mutex_unlock(&sl->master->mutex);
+ return count;
+}
+
+static ssize_t w1_default_read(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);
+
+ mutex_lock(&sl->master->mutex);
+ w1_read_block(sl->master, buf, count);
+ mutex_unlock(&sl->master->mutex);
+ return count;
+}
+
+static struct bin_attribute w1_default_attr = {
+ .attr = {
+ .name = "rw",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .size = PAGE_SIZE,
+ .read = w1_default_read,
+ .write = w1_default_write,
+};
+
+static int w1_default_add_slave(struct w1_slave *sl)
+{
+ return sysfs_create_bin_file(&sl->dev.kobj, &w1_default_attr);
+}
+
+static void w1_default_remove_slave(struct w1_slave *sl)
+{
+ sysfs_remove_bin_file(&sl->dev.kobj, &w1_default_attr);
+}
+
+static struct w1_family_ops w1_default_fops = {
+ .add_slave = w1_default_add_slave,
+ .remove_slave = w1_default_remove_slave,
+};
+
+static struct w1_family w1_default_family = {
+ .fops = &w1_default_fops,
+};
+
+static int w1_uevent(struct device *dev, struct kobj_uevent_env *env);
+
+static struct bus_type w1_bus_type = {
+ .name = "w1",
+ .match = w1_master_match,
+ .uevent = w1_uevent,
+};
+
+struct device_driver w1_master_driver = {
+ .name = "w1_master_driver",
+ .bus = &w1_bus_type,
+ .probe = w1_master_probe,
+};
+
+struct device w1_master_device = {
+ .parent = NULL,
+ .bus = &w1_bus_type,
+ .bus_id = "w1 bus master",
+ .driver = &w1_master_driver,
+ .release = &w1_master_release
+};
+
+static struct device_driver w1_slave_driver = {
+ .name = "w1_slave_driver",
+ .bus = &w1_bus_type,
+};
+
+#if 0
+struct device w1_slave_device = {
+ .parent = NULL,
+ .bus = &w1_bus_type,
+ .bus_id = "w1 bus slave",
+ .driver = &w1_slave_driver,
+ .release = &w1_slave_release
+};
+#endif /* 0 */
+
+static ssize_t w1_master_attribute_show_name(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w1_master *md = dev_to_w1_master(dev);
+ ssize_t count;
+
+ mutex_lock(&md->mutex);
+ count = sprintf(buf, "%s\n", md->name);
+ mutex_unlock(&md->mutex);
+
+ return count;
+}
+
+static ssize_t w1_master_attribute_store_search(struct device * dev,
+ struct device_attribute *attr,
+ const char * buf, size_t count)
+{
+ long tmp;
+ struct w1_master *md = dev_to_w1_master(dev);
+
+ if (strict_strtol(buf, 0, &tmp) == -EINVAL)
+ return -EINVAL;
+
+ mutex_lock(&md->mutex);
+ md->search_count = tmp;
+ mutex_unlock(&md->mutex);
+ wake_up_process(md->thread);
+
+ return count;
+}
+
+static ssize_t w1_master_attribute_show_search(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct w1_master *md = dev_to_w1_master(dev);
+ ssize_t count;
+
+ mutex_lock(&md->mutex);
+ count = sprintf(buf, "%d\n", md->search_count);
+ mutex_unlock(&md->mutex);
+
+ return count;
+}
+
+static ssize_t w1_master_attribute_store_pullup(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ long tmp;
+ struct w1_master *md = dev_to_w1_master(dev);
+
+ if (strict_strtol(buf, 0, &tmp) == -EINVAL)
+ return -EINVAL;
+
+ mutex_lock(&md->mutex);
+ md->enable_pullup = tmp;
+ mutex_unlock(&md->mutex);
+ wake_up_process(md->thread);
+
+ return count;
+}
+
+static ssize_t w1_master_attribute_show_pullup(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct w1_master *md = dev_to_w1_master(dev);
+ ssize_t count;
+
+ mutex_lock(&md->mutex);
+ count = sprintf(buf, "%d\n", md->enable_pullup);
+ mutex_unlock(&md->mutex);
+
+ return count;
+}
+
+static ssize_t w1_master_attribute_show_pointer(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w1_master *md = dev_to_w1_master(dev);
+ ssize_t count;
+
+ mutex_lock(&md->mutex);
+ count = sprintf(buf, "0x%p\n", md->bus_master);
+ mutex_unlock(&md->mutex);
+ return count;
+}
+
+static ssize_t w1_master_attribute_show_timeout(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ ssize_t count;
+ count = sprintf(buf, "%d\n", w1_timeout);
+ return count;
+}
+
+static ssize_t w1_master_attribute_show_max_slave_count(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w1_master *md = dev_to_w1_master(dev);
+ ssize_t count;
+
+ mutex_lock(&md->mutex);
+ count = sprintf(buf, "%d\n", md->max_slave_count);
+ mutex_unlock(&md->mutex);
+ return count;
+}
+
+static ssize_t w1_master_attribute_show_attempts(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w1_master *md = dev_to_w1_master(dev);
+ ssize_t count;
+
+ mutex_lock(&md->mutex);
+ count = sprintf(buf, "%lu\n", md->attempts);
+ mutex_unlock(&md->mutex);
+ return count;
+}
+
+static ssize_t w1_master_attribute_show_slave_count(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w1_master *md = dev_to_w1_master(dev);
+ ssize_t count;
+
+ mutex_lock(&md->mutex);
+ count = sprintf(buf, "%d\n", md->slave_count);
+ mutex_unlock(&md->mutex);
+ return count;
+}
+
+static ssize_t w1_master_attribute_show_slaves(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct w1_master *md = dev_to_w1_master(dev);
+ int c = PAGE_SIZE;
+
+ mutex_lock(&md->mutex);
+
+ if (md->slave_count == 0)
+ c -= snprintf(buf + PAGE_SIZE - c, c, "not found.\n");
+ else {
+ struct list_head *ent, *n;
+ struct w1_slave *sl;
+
+ list_for_each_safe(ent, n, &md->slist) {
+ sl = list_entry(ent, struct w1_slave, w1_slave_entry);
+
+ c -= snprintf(buf + PAGE_SIZE - c, c, "%s\n", sl->name);
+ }
+ }
+
+ mutex_unlock(&md->mutex);
+
+ return PAGE_SIZE - c;
+}
+
+static ssize_t w1_master_attribute_show_add(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int c = PAGE_SIZE;
+ c -= snprintf(buf+PAGE_SIZE - c, c,
+ "write device id xx-xxxxxxxxxxxx to add slave\n");
+ return PAGE_SIZE - c;
+}
+
+static int w1_atoreg_num(struct device *dev, const char *buf, size_t count,
+ struct w1_reg_num *rn)
+{
+ unsigned int family;
+ unsigned long long id;
+ int i;
+ u64 rn64_le;
+
+ /* The CRC value isn't read from the user because the sysfs directory
+ * doesn't include it and most messages from the bus search don't
+ * print it either. It would be unreasonable for the user to then
+ * provide it.
+ */
+ const char *error_msg = "bad slave string format, expecting "
+ "ff-dddddddddddd\n";
+
+ if (buf[2] != '-') {
+ dev_err(dev, "%s", error_msg);
+ return -EINVAL;
+ }
+ i = sscanf(buf, "%02x-%012llx", &family, &id);
+ if (i != 2) {
+ dev_err(dev, "%s", error_msg);
+ return -EINVAL;
+ }
+ rn->family = family;
+ rn->id = id;
+
+ rn64_le = cpu_to_le64(*(u64 *)rn);
+ rn->crc = w1_calc_crc8((u8 *)&rn64_le, 7);
+
+#if 0
+ dev_info(dev, "With CRC device is %02x.%012llx.%02x.\n",
+ rn->family, (unsigned long long)rn->id, rn->crc);
+#endif
+
+ return 0;
+}
+
+/* Searches the slaves in the w1_master and returns a pointer or NULL.
+ * Note: must hold the mutex
+ */
+static struct w1_slave *w1_slave_search_device(struct w1_master *dev,
+ struct w1_reg_num *rn)
+{
+ struct w1_slave *sl;
+ list_for_each_entry(sl, &dev->slist, w1_slave_entry) {
+ if (sl->reg_num.family == rn->family &&
+ sl->reg_num.id == rn->id &&
+ sl->reg_num.crc == rn->crc) {
+ return sl;
+ }
+ }
+ return NULL;
+}
+
+static ssize_t w1_master_attribute_store_add(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct w1_master *md = dev_to_w1_master(dev);
+ struct w1_reg_num rn;
+ struct w1_slave *sl;
+ ssize_t result = count;
+
+ if (w1_atoreg_num(dev, buf, count, &rn))
+ return -EINVAL;
+
+ mutex_lock(&md->mutex);
+ sl = w1_slave_search_device(md, &rn);
+ /* It would be nice to do a targeted search one the one-wire bus
+ * for the new device to see if it is out there or not. But the
+ * current search doesn't support that.
+ */
+ if (sl) {
+ dev_info(dev, "Device %s already exists\n", sl->name);
+ result = -EINVAL;
+ } else {
+ w1_attach_slave_device(md, &rn);
+ }
+ mutex_unlock(&md->mutex);
+
+ return result;
+}
+
+static ssize_t w1_master_attribute_show_remove(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int c = PAGE_SIZE;
+ c -= snprintf(buf+PAGE_SIZE - c, c,
+ "write device id xx-xxxxxxxxxxxx to remove slave\n");
+ return PAGE_SIZE - c;
+}
+
+static ssize_t w1_master_attribute_store_remove(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct w1_master *md = dev_to_w1_master(dev);
+ struct w1_reg_num rn;
+ struct w1_slave *sl;
+ ssize_t result = count;
+
+ if (w1_atoreg_num(dev, buf, count, &rn))
+ return -EINVAL;
+
+ mutex_lock(&md->mutex);
+ sl = w1_slave_search_device(md, &rn);
+ if (sl) {
+ w1_slave_detach(sl);
+ } else {
+ dev_info(dev, "Device %02x-%012llx doesn't exists\n", rn.family,
+ (unsigned long long)rn.id);
+ result = -EINVAL;
+ }
+ mutex_unlock(&md->mutex);
+
+ return result;
+}
+
+#define W1_MASTER_ATTR_RO(_name, _mode) \
+ struct device_attribute w1_master_attribute_##_name = \
+ __ATTR(w1_master_##_name, _mode, \
+ w1_master_attribute_show_##_name, NULL)
+
+#define W1_MASTER_ATTR_RW(_name, _mode) \
+ struct device_attribute w1_master_attribute_##_name = \
+ __ATTR(w1_master_##_name, _mode, \
+ w1_master_attribute_show_##_name, \
+ w1_master_attribute_store_##_name)
+
+static W1_MASTER_ATTR_RO(name, S_IRUGO);
+static W1_MASTER_ATTR_RO(slaves, S_IRUGO);
+static W1_MASTER_ATTR_RO(slave_count, S_IRUGO);
+static W1_MASTER_ATTR_RO(max_slave_count, S_IRUGO);
+static W1_MASTER_ATTR_RO(attempts, S_IRUGO);
+static W1_MASTER_ATTR_RO(timeout, S_IRUGO);
+static W1_MASTER_ATTR_RO(pointer, S_IRUGO);
+static W1_MASTER_ATTR_RW(search, S_IRUGO | S_IWUGO);
+static W1_MASTER_ATTR_RW(pullup, S_IRUGO | S_IWUGO);
+static W1_MASTER_ATTR_RW(add, S_IRUGO | S_IWUGO);
+static W1_MASTER_ATTR_RW(remove, S_IRUGO | S_IWUGO);
+
+static struct attribute *w1_master_default_attrs[] = {
+ &w1_master_attribute_name.attr,
+ &w1_master_attribute_slaves.attr,
+ &w1_master_attribute_slave_count.attr,
+ &w1_master_attribute_max_slave_count.attr,
+ &w1_master_attribute_attempts.attr,
+ &w1_master_attribute_timeout.attr,
+ &w1_master_attribute_pointer.attr,
+ &w1_master_attribute_search.attr,
+ &w1_master_attribute_pullup.attr,
+ &w1_master_attribute_add.attr,
+ &w1_master_attribute_remove.attr,
+ NULL
+};
+
+static struct attribute_group w1_master_defattr_group = {
+ .attrs = w1_master_default_attrs,
+};
+
+int w1_create_master_attributes(struct w1_master *master)
+{
+ return sysfs_create_group(&master->dev.kobj, &w1_master_defattr_group);
+}
+
+void w1_destroy_master_attributes(struct w1_master *master)
+{
+ sysfs_remove_group(&master->dev.kobj, &w1_master_defattr_group);
+}
+
+#ifdef CONFIG_HOTPLUG
+static int w1_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct w1_master *md = NULL;
+ struct w1_slave *sl = NULL;
+ char *event_owner, *name;
+ int err;
+
+ if (dev->driver == &w1_master_driver) {
+ md = container_of(dev, struct w1_master, dev);
+ event_owner = "master";
+ name = md->name;
+ } else if (dev->driver == &w1_slave_driver) {
+ sl = container_of(dev, struct w1_slave, dev);
+ event_owner = "slave";
+ name = sl->name;
+ } else {
+ dev_dbg(dev, "Unknown event.\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "Hotplug event for %s %s, bus_id=%s.\n",
+ event_owner, name, dev->bus_id);
+
+ if (dev->driver != &w1_slave_driver || !sl)
+ return 0;
+
+ err = add_uevent_var(env, "W1_FID=%02X", sl->reg_num.family);
+ if (err)
+ return err;
+
+ err = add_uevent_var(env, "W1_SLAVE_ID=%024LX",
+ (unsigned long long)sl->reg_num.id);
+ if (err)
+ return err;
+
+ return 0;
+};
+#else
+static int w1_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ return 0;
+}
+#endif
+
+static int __w1_attach_slave_device(struct w1_slave *sl)
+{
+ int err;
+
+ sl->dev.parent = &sl->master->dev;
+ sl->dev.driver = &w1_slave_driver;
+ sl->dev.bus = &w1_bus_type;
+ sl->dev.release = &w1_slave_release;
+
+ snprintf(&sl->dev.bus_id[0], sizeof(sl->dev.bus_id),
+ "%02x-%012llx",
+ (unsigned int) sl->reg_num.family,
+ (unsigned long long) sl->reg_num.id);
+ snprintf(&sl->name[0], sizeof(sl->name),
+ "%02x-%012llx",
+ (unsigned int) sl->reg_num.family,
+ (unsigned long long) sl->reg_num.id);
+
+ dev_dbg(&sl->dev, "%s: registering %s as %p.\n", __func__,
+ &sl->dev.bus_id[0], sl);
+
+ err = device_register(&sl->dev);
+ if (err < 0) {
+ dev_err(&sl->dev,
+ "Device registration [%s] failed. err=%d\n",
+ sl->dev.bus_id, err);
+ return err;
+ }
+
+ /* Create "name" entry */
+ err = device_create_file(&sl->dev, &w1_slave_attr_name);
+ if (err < 0) {
+ dev_err(&sl->dev,
+ "sysfs file creation for [%s] failed. err=%d\n",
+ sl->dev.bus_id, err);
+ goto out_unreg;
+ }
+
+ /* Create "id" entry */
+ err = device_create_file(&sl->dev, &w1_slave_attr_id);
+ if (err < 0) {
+ dev_err(&sl->dev,
+ "sysfs file creation for [%s] failed. err=%d\n",
+ sl->dev.bus_id, err);
+ goto out_rem1;
+ }
+
+ /* if the family driver needs to initialize something... */
+ if (sl->family->fops && sl->family->fops->add_slave &&
+ ((err = sl->family->fops->add_slave(sl)) < 0)) {
+ dev_err(&sl->dev,
+ "sysfs file creation for [%s] failed. err=%d\n",
+ sl->dev.bus_id, err);
+ goto out_rem2;
+ }
+
+ list_add_tail(&sl->w1_slave_entry, &sl->master->slist);
+
+ return 0;
+
+out_rem2:
+ device_remove_file(&sl->dev, &w1_slave_attr_id);
+out_rem1:
+ device_remove_file(&sl->dev, &w1_slave_attr_name);
+out_unreg:
+ device_unregister(&sl->dev);
+ return err;
+}
+
+static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn)
+{
+ struct w1_slave *sl;
+ struct w1_family *f;
+ int err;
+ struct w1_netlink_msg msg;
+
+ sl = kzalloc(sizeof(struct w1_slave), GFP_KERNEL);
+ if (!sl) {
+ dev_err(&dev->dev,
+ "%s: failed to allocate new slave device.\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+
+ sl->owner = THIS_MODULE;
+ sl->master = dev;
+ set_bit(W1_SLAVE_ACTIVE, (long *)&sl->flags);
+
+ memset(&msg, 0, sizeof(msg));
+ memcpy(&sl->reg_num, rn, sizeof(sl->reg_num));
+ atomic_set(&sl->refcnt, 0);
+ init_completion(&sl->released);
+
+ spin_lock(&w1_flock);
+ f = w1_family_registered(rn->family);
+ if (!f) {
+ f= &w1_default_family;
+ dev_info(&dev->dev, "Family %x for %02x.%012llx.%02x is not registered.\n",
+ rn->family, rn->family,
+ (unsigned long long)rn->id, rn->crc);
+ }
+ __w1_family_get(f);
+ spin_unlock(&w1_flock);
+
+ sl->family = f;
+
+
+ err = __w1_attach_slave_device(sl);
+ if (err < 0) {
+ dev_err(&dev->dev, "%s: Attaching %s failed.\n", __func__,
+ sl->name);
+ w1_family_put(sl->family);
+ kfree(sl);
+ return err;
+ }
+
+ sl->ttl = dev->slave_ttl;
+ dev->slave_count++;
+
+ memcpy(msg.id.id, rn, sizeof(msg.id));
+ msg.type = W1_SLAVE_ADD;
+ w1_netlink_send(dev, &msg);
+
+ return 0;
+}
+
+void w1_slave_detach(struct w1_slave *sl)
+{
+ struct w1_netlink_msg msg;
+
+ dev_dbg(&sl->dev, "%s: detaching %s [%p].\n", __func__, sl->name, sl);
+
+ list_del(&sl->w1_slave_entry);
+
+ if (sl->family->fops && sl->family->fops->remove_slave)
+ sl->family->fops->remove_slave(sl);
+
+ memset(&msg, 0, sizeof(msg));
+ memcpy(msg.id.id, &sl->reg_num, sizeof(msg.id));
+ msg.type = W1_SLAVE_REMOVE;
+ w1_netlink_send(sl->master, &msg);
+
+ device_remove_file(&sl->dev, &w1_slave_attr_id);
+ device_remove_file(&sl->dev, &w1_slave_attr_name);
+ device_unregister(&sl->dev);
+
+ wait_for_completion(&sl->released);
+ kfree(sl);
+}
+
+struct w1_master *w1_search_master_id(u32 id)
+{
+ struct w1_master *dev;
+ int found = 0;
+
+ mutex_lock(&w1_mlock);
+ list_for_each_entry(dev, &w1_masters, w1_master_entry) {
+ if (dev->id == id) {
+ found = 1;
+ atomic_inc(&dev->refcnt);
+ break;
+ }
+ }
+ mutex_unlock(&w1_mlock);
+
+ return (found)?dev:NULL;
+}
+
+struct w1_slave *w1_search_slave(struct w1_reg_num *id)
+{
+ struct w1_master *dev;
+ struct w1_slave *sl = NULL;
+ int found = 0;
+
+ mutex_lock(&w1_mlock);
+ list_for_each_entry(dev, &w1_masters, w1_master_entry) {
+ mutex_lock(&dev->mutex);
+ list_for_each_entry(sl, &dev->slist, w1_slave_entry) {
+ if (sl->reg_num.family == id->family &&
+ sl->reg_num.id == id->id &&
+ sl->reg_num.crc == id->crc) {
+ found = 1;
+ atomic_inc(&dev->refcnt);
+ atomic_inc(&sl->refcnt);
+ break;
+ }
+ }
+ mutex_unlock(&dev->mutex);
+
+ if (found)
+ break;
+ }
+ mutex_unlock(&w1_mlock);
+
+ return (found)?sl:NULL;
+}
+
+void w1_reconnect_slaves(struct w1_family *f, int attach)
+{
+ struct w1_slave *sl, *sln;
+ struct w1_master *dev;
+
+ mutex_lock(&w1_mlock);
+ list_for_each_entry(dev, &w1_masters, w1_master_entry) {
+ dev_dbg(&dev->dev, "Reconnecting slaves in device %s "
+ "for family %02x.\n", dev->name, f->fid);
+ mutex_lock(&dev->mutex);
+ list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) {
+ /* If it is a new family, slaves with the default
+ * family driver and are that family will be
+ * connected. If the family is going away, devices
+ * matching that family are reconneced.
+ */
+ if ((attach && sl->family->fid == W1_FAMILY_DEFAULT
+ && sl->reg_num.family == f->fid) ||
+ (!attach && sl->family->fid == f->fid)) {
+ struct w1_reg_num rn;
+
+ memcpy(&rn, &sl->reg_num, sizeof(rn));
+ w1_slave_detach(sl);
+
+ w1_attach_slave_device(dev, &rn);
+ }
+ }
+ dev_dbg(&dev->dev, "Reconnecting slaves in device %s "
+ "has been finished.\n", dev->name);
+ mutex_unlock(&dev->mutex);
+ }
+ mutex_unlock(&w1_mlock);
+}
+
+static void w1_slave_found(struct w1_master *dev, u64 rn)
+{
+ struct w1_slave *sl;
+ struct w1_reg_num *tmp;
+ u64 rn_le = cpu_to_le64(rn);
+
+ atomic_inc(&dev->refcnt);
+
+ tmp = (struct w1_reg_num *) &rn;
+
+ sl = w1_slave_search_device(dev, tmp);
+ if (sl) {
+ set_bit(W1_SLAVE_ACTIVE, (long *)&sl->flags);
+ } else {
+ if (rn && tmp->crc == w1_calc_crc8((u8 *)&rn_le, 7))
+ w1_attach_slave_device(dev, tmp);
+ }
+
+ atomic_dec(&dev->refcnt);
+}
+
+/**
+ * Performs a ROM Search & registers any devices found.
+ * The 1-wire search is a simple binary tree search.
+ * For each bit of the address, we read two bits and write one bit.
+ * The bit written will put to sleep all devies that don't match that bit.
+ * When the two reads differ, the direction choice is obvious.
+ * When both bits are 0, we must choose a path to take.
+ * When we can scan all 64 bits without having to choose a path, we are done.
+ *
+ * See "Application note 187 1-wire search algorithm" at www.maxim-ic.com
+ *
+ * @dev The master device to search
+ * @cb Function to call when a device is found
+ */
+void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb)
+{
+ u64 last_rn, rn, tmp64;
+ int i, slave_count = 0;
+ int last_zero, last_device;
+ int search_bit, desc_bit;
+ u8 triplet_ret = 0;
+
+ search_bit = 0;
+ rn = last_rn = 0;
+ last_device = 0;
+ last_zero = -1;
+
+ desc_bit = 64;
+
+ while ( !last_device && (slave_count++ < dev->max_slave_count) ) {
+ last_rn = rn;
+ rn = 0;
+
+ /*
+ * 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 (w1_reset_bus(dev)) {
+ dev_dbg(&dev->dev, "No devices present on the wire.\n");
+ break;
+ }
+
+ /* Start the search */
+ w1_write_8(dev, search_type);
+ for (i = 0; i < 64; ++i) {
+ /* Determine the direction/search bit */
+ if (i == desc_bit)
+ search_bit = 1; /* took the 0 path last time, so take the 1 path */
+ else if (i > desc_bit)
+ search_bit = 0; /* take the 0 path on the next branch */
+ else
+ search_bit = ((last_rn >> i) & 0x1);
+
+ /** Read two bits and write one bit */
+ triplet_ret = w1_triplet(dev, search_bit);
+
+ /* quit if no device responded */
+ if ( (triplet_ret & 0x03) == 0x03 )
+ break;
+
+ /* If both directions were valid, and we took the 0 path... */
+ if (triplet_ret == 0)
+ last_zero = i;
+
+ /* extract the direction taken & update the device number */
+ tmp64 = (triplet_ret >> 2);
+ rn |= (tmp64 << i);
+
+ if (kthread_should_stop()) {
+ dev_dbg(&dev->dev, "Abort w1_search\n");
+ return;
+ }
+ }
+
+ if ( (triplet_ret & 0x03) != 0x03 ) {
+ if ( (desc_bit == last_zero) || (last_zero < 0))
+ last_device = 1;
+ desc_bit = last_zero;
+ cb(dev, rn);
+ }
+ }
+}
+
+void w1_search_process(struct w1_master *dev, u8 search_type)
+{
+ struct w1_slave *sl, *sln;
+
+ list_for_each_entry(sl, &dev->slist, w1_slave_entry)
+ clear_bit(W1_SLAVE_ACTIVE, (long *)&sl->flags);
+
+ w1_search_devices(dev, search_type, w1_slave_found);
+
+ list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) {
+ if (!test_bit(W1_SLAVE_ACTIVE, (unsigned long *)&sl->flags) && !--sl->ttl)
+ w1_slave_detach(sl);
+ else if (test_bit(W1_SLAVE_ACTIVE, (unsigned long *)&sl->flags))
+ sl->ttl = dev->slave_ttl;
+ }
+
+ if (dev->search_count > 0)
+ dev->search_count--;
+}
+
+int w1_process(void *data)
+{
+ struct w1_master *dev = (struct w1_master *) data;
+ /* As long as w1_timeout is only set by a module parameter the sleep
+ * time can be calculated in jiffies once.
+ */
+ const unsigned long jtime = msecs_to_jiffies(w1_timeout * 1000);
+
+ while (!kthread_should_stop()) {
+ if (dev->search_count) {
+ mutex_lock(&dev->mutex);
+ w1_search_process(dev, W1_SEARCH);
+ mutex_unlock(&dev->mutex);
+ }
+
+ try_to_freeze();
+ __set_current_state(TASK_INTERRUPTIBLE);
+
+ if (kthread_should_stop())
+ break;
+
+ /* Only sleep when the search is active. */
+ if (dev->search_count)
+ schedule_timeout(jtime);
+ else
+ schedule();
+ }
+
+ atomic_dec(&dev->refcnt);
+
+ return 0;
+}
+
+static int w1_init(void)
+{
+ int retval;
+
+ printk(KERN_INFO "Driver for 1-wire Dallas network protocol.\n");
+
+ w1_init_netlink();
+
+ retval = bus_register(&w1_bus_type);
+ if (retval) {
+ printk(KERN_ERR "Failed to register bus. err=%d.\n", retval);
+ goto err_out_exit_init;
+ }
+
+ retval = driver_register(&w1_master_driver);
+ if (retval) {
+ printk(KERN_ERR
+ "Failed to register master driver. err=%d.\n",
+ retval);
+ goto err_out_bus_unregister;
+ }
+
+ retval = driver_register(&w1_slave_driver);
+ if (retval) {
+ printk(KERN_ERR
+ "Failed to register master driver. err=%d.\n",
+ retval);
+ goto err_out_master_unregister;
+ }
+
+ return 0;
+
+#if 0
+/* For undoing the slave register if there was a step after it. */
+err_out_slave_unregister:
+ driver_unregister(&w1_slave_driver);
+#endif
+
+err_out_master_unregister:
+ driver_unregister(&w1_master_driver);
+
+err_out_bus_unregister:
+ bus_unregister(&w1_bus_type);
+
+err_out_exit_init:
+ return retval;
+}
+
+static void w1_fini(void)
+{
+ struct w1_master *dev;
+
+ /* Set netlink removal messages and some cleanup */
+ list_for_each_entry(dev, &w1_masters, w1_master_entry)
+ __w1_remove_master_device(dev);
+
+ w1_fini_netlink();
+
+ driver_unregister(&w1_slave_driver);
+ driver_unregister(&w1_master_driver);
+ bus_unregister(&w1_bus_type);
+}
+
+module_init(w1_init);
+module_exit(w1_fini);
diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h
new file mode 100644
index 0000000..97304bd
--- /dev/null
+++ b/drivers/w1/w1.h
@@ -0,0 +1,243 @@
+/*
+ * w1.h
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms 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
+ */
+
+#ifndef __W1_H
+#define __W1_H
+
+struct w1_reg_num
+{
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u64 family:8,
+ id:48,
+ crc:8;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ __u64 crc:8,
+ id:48,
+ family:8;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+};
+
+#ifdef __KERNEL__
+
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+
+#include "w1_family.h"
+
+#define W1_MAXNAMELEN 32
+
+#define W1_SEARCH 0xF0
+#define W1_ALARM_SEARCH 0xEC
+#define W1_CONVERT_TEMP 0x44
+#define W1_SKIP_ROM 0xCC
+#define W1_READ_SCRATCHPAD 0xBE
+#define W1_READ_ROM 0x33
+#define W1_READ_PSUPPLY 0xB4
+#define W1_MATCH_ROM 0x55
+
+#define W1_SLAVE_ACTIVE 0
+
+struct w1_slave
+{
+ struct module *owner;
+ unsigned char name[W1_MAXNAMELEN];
+ struct list_head w1_slave_entry;
+ struct w1_reg_num reg_num;
+ atomic_t refcnt;
+ u8 rom[9];
+ u32 flags;
+ int ttl;
+
+ struct w1_master *master;
+ struct w1_family *family;
+ void *family_data;
+ struct device dev;
+ struct completion released;
+};
+
+typedef void (*w1_slave_found_callback)(struct w1_master *, u64);
+
+
+/**
+ * Note: read_bit and write_bit are very low level functions and should only
+ * be used with hardware that doesn't really support 1-wire operations,
+ * like a parallel/serial port.
+ * Either define read_bit and write_bit OR define, at minimum, touch_bit and
+ * reset_bus.
+ */
+struct w1_bus_master
+{
+ /** the first parameter in all the functions below */
+ void *data;
+
+ /**
+ * Sample the line level
+ * @return the level read (0 or 1)
+ */
+ u8 (*read_bit)(void *);
+
+ /** Sets the line level */
+ void (*write_bit)(void *, u8);
+
+ /**
+ * touch_bit is the lowest-level function for devices that really
+ * support the 1-wire protocol.
+ * touch_bit(0) = write-0 cycle
+ * touch_bit(1) = write-1 / read cycle
+ * @return the bit read (0 or 1)
+ */
+ u8 (*touch_bit)(void *, u8);
+
+ /**
+ * Reads a bytes. Same as 8 touch_bit(1) calls.
+ * @return the byte read
+ */
+ u8 (*read_byte)(void *);
+
+ /**
+ * Writes a byte. Same as 8 touch_bit(x) calls.
+ */
+ void (*write_byte)(void *, u8);
+
+ /**
+ * Same as a series of read_byte() calls
+ * @return the number of bytes read
+ */
+ u8 (*read_block)(void *, u8 *, int);
+
+ /** Same as a series of write_byte() calls */
+ void (*write_block)(void *, const u8 *, int);
+
+ /**
+ * Combines two reads and a smart write for ROM searches
+ * @return bit0=Id bit1=comp_id bit2=dir_taken
+ */
+ u8 (*triplet)(void *, u8);
+
+ /**
+ * long write-0 with a read for the presence pulse detection
+ * @return -1=Error, 0=Device present, 1=No device present
+ */
+ u8 (*reset_bus)(void *);
+
+ /**
+ * Put out a strong pull-up pulse of the specified duration.
+ * @return -1=Error, 0=completed
+ */
+ u8 (*set_pullup)(void *, int);
+
+ /** Really nice hardware can handles the different types of ROM search
+ * w1_master* is passed to the slave found callback.
+ */
+ void (*search)(void *, struct w1_master *,
+ u8, w1_slave_found_callback);
+};
+
+struct w1_master
+{
+ struct list_head w1_master_entry;
+ struct module *owner;
+ unsigned char name[W1_MAXNAMELEN];
+ struct list_head slist;
+ int max_slave_count, slave_count;
+ unsigned long attempts;
+ int slave_ttl;
+ int initialized;
+ u32 id;
+ int search_count;
+
+ atomic_t refcnt;
+
+ void *priv;
+ int priv_size;
+
+ /** 5V strong pullup enabled flag, 1 enabled, zero disabled. */
+ int enable_pullup;
+ /** 5V strong pullup duration in milliseconds, zero disabled. */
+ int pullup_duration;
+
+ struct task_struct *thread;
+ struct mutex mutex;
+
+ struct device_driver *driver;
+ struct device dev;
+
+ struct w1_bus_master *bus_master;
+
+ u32 seq;
+};
+
+int w1_create_master_attributes(struct w1_master *);
+void w1_destroy_master_attributes(struct w1_master *master);
+void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb);
+void w1_search_devices(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb);
+struct w1_slave *w1_search_slave(struct w1_reg_num *id);
+void w1_search_process(struct w1_master *dev, u8 search_type);
+struct w1_master *w1_search_master_id(u32 id);
+
+/* Disconnect and reconnect devices in the given family. Used for finding
+ * unclaimed devices after a family has been registered or releasing devices
+ * after a family has been unregistered. Set attach to 1 when a new family
+ * has just been registered, to 0 when it has been unregistered.
+ */
+void w1_reconnect_slaves(struct w1_family *f, int attach);
+void w1_slave_detach(struct w1_slave *sl);
+
+u8 w1_triplet(struct w1_master *dev, int bdir);
+void w1_write_8(struct w1_master *, u8);
+u8 w1_read_8(struct w1_master *);
+int w1_reset_bus(struct w1_master *);
+u8 w1_calc_crc8(u8 *, int);
+void w1_write_block(struct w1_master *, const u8 *, int);
+u8 w1_read_block(struct w1_master *, u8 *, int);
+int w1_reset_select_slave(struct w1_slave *sl);
+void w1_next_pullup(struct w1_master *, int);
+
+static inline struct w1_slave* dev_to_w1_slave(struct device *dev)
+{
+ return container_of(dev, struct w1_slave, dev);
+}
+
+static inline struct w1_slave* kobj_to_w1_slave(struct kobject *kobj)
+{
+ return dev_to_w1_slave(container_of(kobj, struct device, kobj));
+}
+
+static inline struct w1_master* dev_to_w1_master(struct device *dev)
+{
+ return container_of(dev, struct w1_master, dev);
+}
+
+extern struct device_driver w1_master_driver;
+extern struct device w1_master_device;
+extern int w1_max_slave_count;
+extern int w1_max_slave_ttl;
+extern struct list_head w1_masters;
+extern struct mutex w1_mlock;
+
+extern int w1_process(void *);
+
+#endif /* __KERNEL__ */
+
+#endif /* __W1_H */
diff --git a/drivers/w1/w1_family.c b/drivers/w1/w1_family.c
new file mode 100644
index 0000000..4a09904
--- /dev/null
+++ b/drivers/w1/w1_family.c
@@ -0,0 +1,139 @@
+/*
+ * w1_family.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms 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/spinlock.h>
+#include <linux/list.h>
+#include <linux/sched.h> /* schedule_timeout() */
+#include <linux/delay.h>
+
+#include "w1_family.h"
+#include "w1.h"
+
+DEFINE_SPINLOCK(w1_flock);
+static LIST_HEAD(w1_families);
+
+int w1_register_family(struct w1_family *newf)
+{
+ struct list_head *ent, *n;
+ struct w1_family *f;
+ int ret = 0;
+
+ spin_lock(&w1_flock);
+ list_for_each_safe(ent, n, &w1_families) {
+ f = list_entry(ent, struct w1_family, family_entry);
+
+ if (f->fid == newf->fid) {
+ ret = -EEXIST;
+ break;
+ }
+ }
+
+ if (!ret) {
+ atomic_set(&newf->refcnt, 0);
+ list_add_tail(&newf->family_entry, &w1_families);
+ }
+ spin_unlock(&w1_flock);
+
+ /* check default devices against the new set of drivers */
+ w1_reconnect_slaves(newf, 1);
+
+ return ret;
+}
+
+void w1_unregister_family(struct w1_family *fent)
+{
+ struct list_head *ent, *n;
+ struct w1_family *f;
+
+ spin_lock(&w1_flock);
+ list_for_each_safe(ent, n, &w1_families) {
+ f = list_entry(ent, struct w1_family, family_entry);
+
+ if (f->fid == fent->fid) {
+ list_del(&fent->family_entry);
+ break;
+ }
+ }
+ spin_unlock(&w1_flock);
+
+ /* deatch devices using this family code */
+ w1_reconnect_slaves(fent, 0);
+
+ while (atomic_read(&fent->refcnt)) {
+ printk(KERN_INFO "Waiting for family %u to become free: refcnt=%d.\n",
+ fent->fid, atomic_read(&fent->refcnt));
+
+ if (msleep_interruptible(1000))
+ flush_signals(current);
+ }
+}
+
+/*
+ * Should be called under w1_flock held.
+ */
+struct w1_family * w1_family_registered(u8 fid)
+{
+ struct list_head *ent, *n;
+ struct w1_family *f = NULL;
+ int ret = 0;
+
+ list_for_each_safe(ent, n, &w1_families) {
+ f = list_entry(ent, struct w1_family, family_entry);
+
+ if (f->fid == fid) {
+ ret = 1;
+ break;
+ }
+ }
+
+ return (ret) ? f : NULL;
+}
+
+static void __w1_family_put(struct w1_family *f)
+{
+ atomic_dec(&f->refcnt);
+}
+
+void w1_family_put(struct w1_family *f)
+{
+ spin_lock(&w1_flock);
+ __w1_family_put(f);
+ spin_unlock(&w1_flock);
+}
+
+#if 0
+void w1_family_get(struct w1_family *f)
+{
+ spin_lock(&w1_flock);
+ __w1_family_get(f);
+ spin_unlock(&w1_flock);
+}
+#endif /* 0 */
+
+void __w1_family_get(struct w1_family *f)
+{
+ smp_mb__before_atomic_inc();
+ atomic_inc(&f->refcnt);
+ smp_mb__after_atomic_inc();
+}
+
+EXPORT_SYMBOL(w1_unregister_family);
+EXPORT_SYMBOL(w1_register_family);
diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h
new file mode 100644
index 0000000..3ca1b92
--- /dev/null
+++ b/drivers/w1/w1_family.h
@@ -0,0 +1,67 @@
+/*
+ * w1_family.h
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms 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
+ */
+
+#ifndef __W1_FAMILY_H
+#define __W1_FAMILY_H
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <asm/atomic.h>
+
+#define W1_FAMILY_DEFAULT 0
+#define W1_FAMILY_SMEM_01 0x01
+#define W1_FAMILY_SMEM_81 0x81
+#define W1_THERM_DS18S20 0x10
+#define W1_THERM_DS1822 0x22
+#define W1_EEPROM_DS2433 0x23
+#define W1_THERM_DS18B20 0x28
+#define W1_EEPROM_DS2431 0x2D
+#define W1_FAMILY_DS2760 0x30
+
+#define MAXNAMELEN 32
+
+struct w1_slave;
+
+struct w1_family_ops
+{
+ int (* add_slave)(struct w1_slave *);
+ void (* remove_slave)(struct w1_slave *);
+};
+
+struct w1_family
+{
+ struct list_head family_entry;
+ u8 fid;
+
+ struct w1_family_ops *fops;
+
+ atomic_t refcnt;
+};
+
+extern spinlock_t w1_flock;
+
+void w1_family_put(struct w1_family *);
+void __w1_family_get(struct w1_family *);
+struct w1_family * w1_family_registered(u8);
+void w1_unregister_family(struct w1_family *);
+int w1_register_family(struct w1_family *);
+
+#endif /* __W1_FAMILY_H */
diff --git a/drivers/w1/w1_int.c b/drivers/w1/w1_int.c
new file mode 100644
index 0000000..a3a5456
--- /dev/null
+++ b/drivers/w1/w1_int.c
@@ -0,0 +1,250 @@
+/*
+ * w1_int.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms 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/list.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+
+#include "w1.h"
+#include "w1_log.h"
+#include "w1_netlink.h"
+#include "w1_int.h"
+
+static int w1_search_count = -1; /* Default is continual scan */
+module_param_named(search_count, w1_search_count, int, 0);
+
+static int w1_enable_pullup = 1;
+module_param_named(enable_pullup, w1_enable_pullup, int, 0);
+
+static struct w1_master * w1_alloc_dev(u32 id, int slave_count, int slave_ttl,
+ struct device_driver *driver,
+ struct device *device)
+{
+ struct w1_master *dev;
+ int err;
+
+ /*
+ * We are in process context(kernel thread), so can sleep.
+ */
+ dev = kzalloc(sizeof(struct w1_master) + sizeof(struct w1_bus_master), GFP_KERNEL);
+ if (!dev) {
+ printk(KERN_ERR
+ "Failed to allocate %zd bytes for new w1 device.\n",
+ sizeof(struct w1_master));
+ return NULL;
+ }
+
+
+ dev->bus_master = (struct w1_bus_master *)(dev + 1);
+
+ dev->owner = THIS_MODULE;
+ dev->max_slave_count = slave_count;
+ dev->slave_count = 0;
+ dev->attempts = 0;
+ dev->initialized = 0;
+ dev->id = id;
+ dev->slave_ttl = slave_ttl;
+ dev->search_count = w1_search_count;
+ dev->enable_pullup = w1_enable_pullup;
+
+ /* 1 for w1_process to decrement
+ * 1 for __w1_remove_master_device to decrement
+ */
+ atomic_set(&dev->refcnt, 2);
+
+ INIT_LIST_HEAD(&dev->slist);
+ mutex_init(&dev->mutex);
+
+ memcpy(&dev->dev, device, sizeof(struct device));
+ snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
+ "w1_bus_master%u", dev->id);
+ snprintf(dev->name, sizeof(dev->name), "w1_bus_master%u", dev->id);
+
+ dev->driver = driver;
+
+ dev->seq = 1;
+
+ err = device_register(&dev->dev);
+ if (err) {
+ printk(KERN_ERR "Failed to register master device. err=%d\n", err);
+ memset(dev, 0, sizeof(struct w1_master));
+ kfree(dev);
+ dev = NULL;
+ }
+
+ return dev;
+}
+
+static void w1_free_dev(struct w1_master *dev)
+{
+ device_unregister(&dev->dev);
+}
+
+int w1_add_master_device(struct w1_bus_master *master)
+{
+ struct w1_master *dev, *entry;
+ int retval = 0;
+ struct w1_netlink_msg msg;
+ int id, found;
+
+ /* validate minimum functionality */
+ if (!(master->touch_bit && master->reset_bus) &&
+ !(master->write_bit && master->read_bit) &&
+ !(master->write_byte && master->read_byte && master->reset_bus)) {
+ printk(KERN_ERR "w1_add_master_device: invalid function set\n");
+ return(-EINVAL);
+ }
+ /* While it would be electrically possible to make a device that
+ * generated a strong pullup in bit bang mode, only hardare that
+ * controls 1-wire time frames are even expected to support a strong
+ * pullup. w1_io.c would need to support calling set_pullup before
+ * the last write_bit operation of a w1_write_8 which it currently
+ * doesn't.
+ */
+ if (!master->write_byte && !master->touch_bit && master->set_pullup) {
+ printk(KERN_ERR "w1_add_master_device: set_pullup requires "
+ "write_byte or touch_bit, disabling\n");
+ master->set_pullup = NULL;
+ }
+
+ /* Lock until the device is added (or not) to w1_masters. */
+ mutex_lock(&w1_mlock);
+ /* Search for the first available id (starting at 1). */
+ id = 0;
+ do {
+ ++id;
+ found = 0;
+ list_for_each_entry(entry, &w1_masters, w1_master_entry) {
+ if (entry->id == id) {
+ found = 1;
+ break;
+ }
+ }
+ } while (found);
+
+ dev = w1_alloc_dev(id, w1_max_slave_count, w1_max_slave_ttl,
+ &w1_master_driver, &w1_master_device);
+ if (!dev) {
+ mutex_unlock(&w1_mlock);
+ return -ENOMEM;
+ }
+
+ retval = w1_create_master_attributes(dev);
+ if (retval) {
+ mutex_unlock(&w1_mlock);
+ goto err_out_free_dev;
+ }
+
+ memcpy(dev->bus_master, master, sizeof(struct w1_bus_master));
+
+ dev->initialized = 1;
+
+ dev->thread = kthread_run(&w1_process, dev, "%s", dev->name);
+ if (IS_ERR(dev->thread)) {
+ retval = PTR_ERR(dev->thread);
+ dev_err(&dev->dev,
+ "Failed to create new kernel thread. err=%d\n",
+ retval);
+ mutex_unlock(&w1_mlock);
+ goto err_out_rm_attr;
+ }
+
+ list_add(&dev->w1_master_entry, &w1_masters);
+ mutex_unlock(&w1_mlock);
+
+ memset(&msg, 0, sizeof(msg));
+ msg.id.mst.id = dev->id;
+ msg.type = W1_MASTER_ADD;
+ w1_netlink_send(dev, &msg);
+
+ return 0;
+
+#if 0 /* Thread cleanup code, not required currently. */
+err_out_kill_thread:
+ kthread_stop(dev->thread);
+#endif
+err_out_rm_attr:
+ w1_destroy_master_attributes(dev);
+err_out_free_dev:
+ w1_free_dev(dev);
+
+ return retval;
+}
+
+void __w1_remove_master_device(struct w1_master *dev)
+{
+ struct w1_netlink_msg msg;
+ struct w1_slave *sl, *sln;
+
+ kthread_stop(dev->thread);
+
+ mutex_lock(&w1_mlock);
+ list_del(&dev->w1_master_entry);
+ mutex_unlock(&w1_mlock);
+
+ mutex_lock(&dev->mutex);
+ list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry)
+ w1_slave_detach(sl);
+ w1_destroy_master_attributes(dev);
+ mutex_unlock(&dev->mutex);
+ atomic_dec(&dev->refcnt);
+
+ while (atomic_read(&dev->refcnt)) {
+ dev_info(&dev->dev, "Waiting for %s to become free: refcnt=%d.\n",
+ dev->name, atomic_read(&dev->refcnt));
+
+ if (msleep_interruptible(1000))
+ flush_signals(current);
+ }
+
+ memset(&msg, 0, sizeof(msg));
+ msg.id.mst.id = dev->id;
+ msg.type = W1_MASTER_REMOVE;
+ w1_netlink_send(dev, &msg);
+
+ w1_free_dev(dev);
+}
+
+void w1_remove_master_device(struct w1_bus_master *bm)
+{
+ struct w1_master *dev, *found = NULL;
+
+ list_for_each_entry(dev, &w1_masters, w1_master_entry) {
+ if (!dev->initialized)
+ continue;
+
+ if (dev->bus_master->data == bm->data) {
+ found = dev;
+ break;
+ }
+ }
+
+ if (!found) {
+ printk(KERN_ERR "Device doesn't exist.\n");
+ return;
+ }
+
+ __w1_remove_master_device(found);
+}
+
+EXPORT_SYMBOL(w1_add_master_device);
+EXPORT_SYMBOL(w1_remove_master_device);
diff --git a/drivers/w1/w1_int.h b/drivers/w1/w1_int.h
new file mode 100644
index 0000000..4274082
--- /dev/null
+++ b/drivers/w1/w1_int.h
@@ -0,0 +1,34 @@
+/*
+ * w1_int.h
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms 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
+ */
+
+#ifndef __W1_INT_H
+#define __W1_INT_H
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+
+#include "w1.h"
+
+int w1_add_master_device(struct w1_bus_master *);
+void w1_remove_master_device(struct w1_bus_master *);
+void __w1_remove_master_device(struct w1_master *);
+
+#endif /* __W1_INT_H */
diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c
new file mode 100644
index 0000000..5139c25
--- /dev/null
+++ b/drivers/w1/w1_io.c
@@ -0,0 +1,383 @@
+/*
+ * w1_io.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms 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 <asm/io.h>
+
+#include <linux/delay.h>
+#include <linux/moduleparam.h>
+#include <linux/module.h>
+
+#include "w1.h"
+#include "w1_log.h"
+
+static int w1_delay_parm = 1;
+module_param_named(delay_coef, w1_delay_parm, int, 0);
+
+static u8 w1_crc8_table[] = {
+ 0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65,
+ 157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220,
+ 35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98,
+ 190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255,
+ 70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7,
+ 219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154,
+ 101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36,
+ 248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185,
+ 140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205,
+ 17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80,
+ 175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238,
+ 50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115,
+ 202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139,
+ 87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22,
+ 233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168,
+ 116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53
+};
+
+static void w1_delay(unsigned long tm)
+{
+ udelay(tm * w1_delay_parm);
+}
+
+static void w1_write_bit(struct w1_master *dev, int bit);
+static u8 w1_read_bit(struct w1_master *dev);
+
+/**
+ * Generates a write-0 or write-1 cycle and samples the level.
+ */
+static u8 w1_touch_bit(struct w1_master *dev, int bit)
+{
+ if (dev->bus_master->touch_bit)
+ return dev->bus_master->touch_bit(dev->bus_master->data, bit);
+ else if (bit)
+ return w1_read_bit(dev);
+ else {
+ w1_write_bit(dev, 0);
+ return(0);
+ }
+}
+
+/**
+ * Generates a write-0 or write-1 cycle.
+ * Only call if dev->bus_master->touch_bit is NULL
+ */
+static void w1_write_bit(struct w1_master *dev, int bit)
+{
+ if (bit) {
+ dev->bus_master->write_bit(dev->bus_master->data, 0);
+ w1_delay(6);
+ dev->bus_master->write_bit(dev->bus_master->data, 1);
+ w1_delay(64);
+ } else {
+ dev->bus_master->write_bit(dev->bus_master->data, 0);
+ w1_delay(60);
+ dev->bus_master->write_bit(dev->bus_master->data, 1);
+ w1_delay(10);
+ }
+}
+
+/**
+ * Pre-write operation, currently only supporting strong pullups.
+ * Program the hardware for a strong pullup, if one has been requested and
+ * the hardware supports it.
+ *
+ * @param dev the master device
+ */
+static void w1_pre_write(struct w1_master *dev)
+{
+ if (dev->pullup_duration &&
+ dev->enable_pullup && dev->bus_master->set_pullup) {
+ dev->bus_master->set_pullup(dev->bus_master->data,
+ dev->pullup_duration);
+ }
+}
+
+/**
+ * Post-write operation, currently only supporting strong pullups.
+ * If a strong pullup was requested, clear it if the hardware supports
+ * them, or execute the delay otherwise, in either case clear the request.
+ *
+ * @param dev the master device
+ */
+static void w1_post_write(struct w1_master *dev)
+{
+ if (dev->pullup_duration) {
+ if (dev->enable_pullup && dev->bus_master->set_pullup)
+ dev->bus_master->set_pullup(dev->bus_master->data, 0);
+ else
+ msleep(dev->pullup_duration);
+ dev->pullup_duration = 0;
+ }
+}
+
+/**
+ * Writes 8 bits.
+ *
+ * @param dev the master device
+ * @param byte the byte to write
+ */
+void w1_write_8(struct w1_master *dev, u8 byte)
+{
+ int i;
+
+ if (dev->bus_master->write_byte) {
+ w1_pre_write(dev);
+ dev->bus_master->write_byte(dev->bus_master->data, byte);
+ }
+ else
+ for (i = 0; i < 8; ++i) {
+ if (i == 7)
+ w1_pre_write(dev);
+ w1_touch_bit(dev, (byte >> i) & 0x1);
+ }
+ w1_post_write(dev);
+}
+EXPORT_SYMBOL_GPL(w1_write_8);
+
+
+/**
+ * Generates a write-1 cycle and samples the level.
+ * Only call if dev->bus_master->touch_bit is NULL
+ */
+static u8 w1_read_bit(struct w1_master *dev)
+{
+ int result;
+
+ dev->bus_master->write_bit(dev->bus_master->data, 0);
+ w1_delay(6);
+ dev->bus_master->write_bit(dev->bus_master->data, 1);
+ w1_delay(9);
+
+ result = dev->bus_master->read_bit(dev->bus_master->data);
+ w1_delay(55);
+
+ return result & 0x1;
+}
+
+/**
+ * Does a triplet - used for searching ROM addresses.
+ * Return bits:
+ * bit 0 = id_bit
+ * bit 1 = comp_bit
+ * bit 2 = dir_taken
+ * If both bits 0 & 1 are set, the search should be restarted.
+ *
+ * @param dev the master device
+ * @param bdir the bit to write if both id_bit and comp_bit are 0
+ * @return bit fields - see above
+ */
+u8 w1_triplet(struct w1_master *dev, int bdir)
+{
+ if ( dev->bus_master->triplet )
+ return(dev->bus_master->triplet(dev->bus_master->data, bdir));
+ else {
+ u8 id_bit = w1_touch_bit(dev, 1);
+ u8 comp_bit = w1_touch_bit(dev, 1);
+ u8 retval;
+
+ if ( id_bit && comp_bit )
+ return(0x03); /* error */
+
+ if ( !id_bit && !comp_bit ) {
+ /* Both bits are valid, take the direction given */
+ retval = bdir ? 0x04 : 0;
+ } else {
+ /* Only one bit is valid, take that direction */
+ bdir = id_bit;
+ retval = id_bit ? 0x05 : 0x02;
+ }
+
+ if ( dev->bus_master->touch_bit )
+ w1_touch_bit(dev, bdir);
+ else
+ w1_write_bit(dev, bdir);
+ return(retval);
+ }
+}
+
+/**
+ * Reads 8 bits.
+ *
+ * @param dev the master device
+ * @return the byte read
+ */
+u8 w1_read_8(struct w1_master *dev)
+{
+ int i;
+ u8 res = 0;
+
+ if (dev->bus_master->read_byte)
+ res = dev->bus_master->read_byte(dev->bus_master->data);
+ else
+ for (i = 0; i < 8; ++i)
+ res |= (w1_touch_bit(dev,1) << i);
+
+ return res;
+}
+EXPORT_SYMBOL_GPL(w1_read_8);
+
+/**
+ * Writes a series of bytes.
+ *
+ * @param dev the master device
+ * @param buf pointer to the data to write
+ * @param len the number of bytes to write
+ * @return the byte read
+ */
+void w1_write_block(struct w1_master *dev, const u8 *buf, int len)
+{
+ int i;
+
+ if (dev->bus_master->write_block) {
+ w1_pre_write(dev);
+ dev->bus_master->write_block(dev->bus_master->data, buf, len);
+ }
+ else
+ for (i = 0; i < len; ++i)
+ w1_write_8(dev, buf[i]); /* calls w1_pre_write */
+ w1_post_write(dev);
+}
+EXPORT_SYMBOL_GPL(w1_write_block);
+
+/**
+ * Reads a series of bytes.
+ *
+ * @param dev the master device
+ * @param buf pointer to the buffer to fill
+ * @param len the number of bytes to read
+ * @return the number of bytes read
+ */
+u8 w1_read_block(struct w1_master *dev, u8 *buf, int len)
+{
+ int i;
+ u8 ret;
+
+ if (dev->bus_master->read_block)
+ ret = dev->bus_master->read_block(dev->bus_master->data, buf, len);
+ else {
+ for (i = 0; i < len; ++i)
+ buf[i] = w1_read_8(dev);
+ ret = len;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(w1_read_block);
+
+/**
+ * Issues a reset bus sequence.
+ *
+ * @param dev The bus master pointer
+ * @return 0=Device present, 1=No device present or error
+ */
+int w1_reset_bus(struct w1_master *dev)
+{
+ int result;
+
+ if (dev->bus_master->reset_bus)
+ result = dev->bus_master->reset_bus(dev->bus_master->data) & 0x1;
+ else {
+ dev->bus_master->write_bit(dev->bus_master->data, 0);
+ /* minimum 480, max ? us
+ * be nice and sleep, except 18b20 spec lists 960us maximum,
+ * so until we can sleep with microsecond accuracy, spin.
+ * Feel free to come up with some other way to give up the
+ * cpu for such a short amount of time AND get it back in
+ * the maximum amount of time.
+ */
+ w1_delay(480);
+ dev->bus_master->write_bit(dev->bus_master->data, 1);
+ w1_delay(70);
+
+ result = dev->bus_master->read_bit(dev->bus_master->data) & 0x1;
+ /* minmum 70 (above) + 410 = 480 us
+ * There aren't any timing requirements between a reset and
+ * the following transactions. Sleeping is safe here.
+ */
+ /* w1_delay(410); min required time */
+ msleep(1);
+ }
+
+ return result;
+}
+EXPORT_SYMBOL_GPL(w1_reset_bus);
+
+u8 w1_calc_crc8(u8 * data, int len)
+{
+ u8 crc = 0;
+
+ while (len--)
+ crc = w1_crc8_table[crc ^ *data++];
+
+ return crc;
+}
+EXPORT_SYMBOL_GPL(w1_calc_crc8);
+
+void w1_search_devices(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb)
+{
+ dev->attempts++;
+ if (dev->bus_master->search)
+ dev->bus_master->search(dev->bus_master->data, dev,
+ search_type, cb);
+ else
+ w1_search(dev, search_type, cb);
+}
+
+/**
+ * Resets the bus and then selects the slave by sending either a skip rom
+ * or a rom match.
+ * The w1 master lock must be held.
+ *
+ * @param sl the slave to select
+ * @return 0=success, anything else=error
+ */
+int w1_reset_select_slave(struct w1_slave *sl)
+{
+ if (w1_reset_bus(sl->master))
+ return -1;
+
+ if (sl->master->slave_count == 1)
+ w1_write_8(sl->master, W1_SKIP_ROM);
+ else {
+ u8 match[9] = {W1_MATCH_ROM, };
+ u64 rn = le64_to_cpu(*((u64*)&sl->reg_num));
+
+ memcpy(&match[1], &rn, 8);
+ w1_write_block(sl->master, match, 9);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(w1_reset_select_slave);
+
+/**
+ * Put out a strong pull-up of the specified duration after the next write
+ * operation. Not all hardware supports strong pullups. Hardware that
+ * doesn't support strong pullups will sleep for the given time after the
+ * write operation without a strong pullup. This is a one shot request for
+ * the next write, specifying zero will clear a previous request.
+ * The w1 master lock must be held.
+ *
+ * @param delay time in milliseconds
+ * @return 0=success, anything else=error
+ */
+void w1_next_pullup(struct w1_master *dev, int delay)
+{
+ dev->pullup_duration = delay;
+}
+EXPORT_SYMBOL_GPL(w1_next_pullup);
diff --git a/drivers/w1/w1_log.h b/drivers/w1/w1_log.h
new file mode 100644
index 0000000..e6ab7cf
--- /dev/null
+++ b/drivers/w1/w1_log.h
@@ -0,0 +1,38 @@
+/*
+ * w1_log.h
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms 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
+ */
+
+#ifndef __W1_LOG_H
+#define __W1_LOG_H
+
+#define DEBUG
+
+#ifdef W1_DEBUG
+# define assert(expr) do {} while (0)
+#else
+# define assert(expr) \
+ if(unlikely(!(expr))) { \
+ printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n", \
+ #expr, __FILE__, __func__, __LINE__); \
+ }
+#endif
+
+#endif /* __W1_LOG_H */
+
diff --git a/drivers/w1/w1_netlink.c b/drivers/w1/w1_netlink.c
new file mode 100644
index 0000000..65c5ebd
--- /dev/null
+++ b/drivers/w1/w1_netlink.c
@@ -0,0 +1,245 @@
+/*
+ * w1_netlink.c
+ *
+ * Copyright (c) 2003 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms 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/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/connector.h>
+
+#include "w1.h"
+#include "w1_log.h"
+#include "w1_netlink.h"
+
+#if defined(CONFIG_W1_CON) && (defined(CONFIG_CONNECTOR) || (defined(CONFIG_CONNECTOR_MODULE) && defined(CONFIG_W1_MODULE)))
+void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg)
+{
+ char buf[sizeof(struct cn_msg) + sizeof(struct w1_netlink_msg)];
+ struct cn_msg *m = (struct cn_msg *)buf;
+ struct w1_netlink_msg *w = (struct w1_netlink_msg *)(m+1);
+
+ memset(buf, 0, sizeof(buf));
+
+ m->id.idx = CN_W1_IDX;
+ m->id.val = CN_W1_VAL;
+
+ m->seq = dev->seq++;
+ m->len = sizeof(struct w1_netlink_msg);
+
+ memcpy(w, msg, sizeof(struct w1_netlink_msg));
+
+ cn_netlink_send(m, 0, GFP_KERNEL);
+}
+
+static int w1_process_command_master(struct w1_master *dev, struct cn_msg *msg,
+ struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd)
+{
+ dev_dbg(&dev->dev, "%s: %s: cmd=%02x, len=%u.\n",
+ __func__, dev->name, cmd->cmd, cmd->len);
+
+ if (cmd->cmd != W1_CMD_SEARCH && cmd->cmd != W1_CMD_ALARM_SEARCH)
+ return -EINVAL;
+
+ w1_search_process(dev, (cmd->cmd == W1_CMD_ALARM_SEARCH)?W1_ALARM_SEARCH:W1_SEARCH);
+ return 0;
+}
+
+static int w1_send_read_reply(struct w1_slave *sl, struct cn_msg *msg,
+ struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd)
+{
+ void *data;
+ struct w1_netlink_msg *h;
+ struct w1_netlink_cmd *c;
+ struct cn_msg *cm;
+ int err;
+
+ data = kzalloc(sizeof(struct cn_msg) +
+ sizeof(struct w1_netlink_msg) +
+ sizeof(struct w1_netlink_cmd) +
+ cmd->len, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ cm = (struct cn_msg *)(data);
+ h = (struct w1_netlink_msg *)(cm + 1);
+ c = (struct w1_netlink_cmd *)(h + 1);
+
+ memcpy(cm, msg, sizeof(struct cn_msg));
+ memcpy(h, hdr, sizeof(struct w1_netlink_msg));
+ memcpy(c, cmd, sizeof(struct w1_netlink_cmd));
+
+ cm->ack = msg->seq+1;
+ cm->len = sizeof(struct w1_netlink_msg) + sizeof(struct w1_netlink_cmd) + cmd->len;
+
+ h->len = sizeof(struct w1_netlink_cmd) + cmd->len;
+
+ memcpy(c->data, cmd->data, c->len);
+
+ err = cn_netlink_send(cm, 0, GFP_KERNEL);
+
+ kfree(data);
+
+ return err;
+}
+
+static int w1_process_command_slave(struct w1_slave *sl, struct cn_msg *msg,
+ struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd)
+{
+ int err = 0;
+
+ dev_dbg(&sl->master->dev, "%s: %02x.%012llx.%02x: cmd=%02x, len=%u.\n",
+ __func__, sl->reg_num.family, (unsigned long long)sl->reg_num.id, sl->reg_num.crc,
+ cmd->cmd, cmd->len);
+
+ switch (cmd->cmd) {
+ case W1_CMD_READ:
+ w1_read_block(sl->master, cmd->data, cmd->len);
+ w1_send_read_reply(sl, msg, hdr, cmd);
+ break;
+ case W1_CMD_WRITE:
+ w1_write_block(sl->master, cmd->data, cmd->len);
+ break;
+ case W1_CMD_SEARCH:
+ case W1_CMD_ALARM_SEARCH:
+ w1_search_process(sl->master,
+ (cmd->cmd == W1_CMD_ALARM_SEARCH)?W1_ALARM_SEARCH:W1_SEARCH);
+ break;
+ default:
+ err = -1;
+ break;
+ }
+
+ return err;
+}
+
+static void w1_cn_callback(void *data)
+{
+ struct cn_msg *msg = data;
+ struct w1_netlink_msg *m = (struct w1_netlink_msg *)(msg + 1);
+ struct w1_netlink_cmd *cmd;
+ struct w1_slave *sl;
+ struct w1_master *dev;
+ int err = 0;
+
+ while (msg->len && !err) {
+ struct w1_reg_num id;
+ u16 mlen = m->len;
+ u8 *cmd_data = m->data;
+
+ dev = NULL;
+ sl = NULL;
+
+ memcpy(&id, m->id.id, sizeof(id));
+#if 0
+ printk("%s: %02x.%012llx.%02x: type=%02x, len=%u.\n",
+ __func__, id.family, (unsigned long long)id.id, id.crc, m->type, m->len);
+#endif
+ if (m->len + sizeof(struct w1_netlink_msg) > msg->len) {
+ err = -E2BIG;
+ break;
+ }
+
+ if (!mlen)
+ goto out_cont;
+
+ if (m->type == W1_MASTER_CMD) {
+ dev = w1_search_master_id(m->id.mst.id);
+ } else if (m->type == W1_SLAVE_CMD) {
+ sl = w1_search_slave(&id);
+ if (sl)
+ dev = sl->master;
+ }
+
+ if (!dev) {
+ err = -ENODEV;
+ goto out_cont;
+ }
+
+ mutex_lock(&dev->mutex);
+
+ if (sl && w1_reset_select_slave(sl)) {
+ err = -ENODEV;
+ goto out_up;
+ }
+
+ while (mlen) {
+ cmd = (struct w1_netlink_cmd *)cmd_data;
+
+ if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) {
+ err = -E2BIG;
+ break;
+ }
+
+ if (sl)
+ w1_process_command_slave(sl, msg, m, cmd);
+ else
+ w1_process_command_master(dev, msg, m, cmd);
+
+ cmd_data += cmd->len + sizeof(struct w1_netlink_cmd);
+ mlen -= cmd->len + sizeof(struct w1_netlink_cmd);
+ }
+out_up:
+ atomic_dec(&dev->refcnt);
+ if (sl)
+ atomic_dec(&sl->refcnt);
+ mutex_unlock(&dev->mutex);
+out_cont:
+ msg->len -= sizeof(struct w1_netlink_msg) + m->len;
+ m = (struct w1_netlink_msg *)(((u8 *)m) + sizeof(struct w1_netlink_msg) + m->len);
+
+ /*
+ * Let's allow requests for nonexisting devices.
+ */
+ if (err == -ENODEV)
+ err = 0;
+ }
+#if 0
+ if (err) {
+ printk("%s: malformed message. Dropping.\n", __func__);
+ }
+#endif
+}
+
+int w1_init_netlink(void)
+{
+ struct cb_id w1_id = {.idx = CN_W1_IDX, .val = CN_W1_VAL};
+
+ return cn_add_callback(&w1_id, "w1", &w1_cn_callback);
+}
+
+void w1_fini_netlink(void)
+{
+ struct cb_id w1_id = {.idx = CN_W1_IDX, .val = CN_W1_VAL};
+
+ cn_del_callback(&w1_id);
+}
+#else
+void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg)
+{
+}
+
+int w1_init_netlink(void)
+{
+ return 0;
+}
+
+void w1_fini_netlink(void)
+{
+}
+#endif
diff --git a/drivers/w1/w1_netlink.h b/drivers/w1/w1_netlink.h
new file mode 100644
index 0000000..56122b9
--- /dev/null
+++ b/drivers/w1/w1_netlink.h
@@ -0,0 +1,74 @@
+/*
+ * w1_netlink.h
+ *
+ * Copyright (c) 2003 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms 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
+ */
+
+#ifndef __W1_NETLINK_H
+#define __W1_NETLINK_H
+
+#include <asm/types.h>
+#include <linux/connector.h>
+
+#include "w1.h"
+
+enum w1_netlink_message_types {
+ W1_SLAVE_ADD = 0,
+ W1_SLAVE_REMOVE,
+ W1_MASTER_ADD,
+ W1_MASTER_REMOVE,
+ W1_MASTER_CMD,
+ W1_SLAVE_CMD,
+};
+
+struct w1_netlink_msg
+{
+ __u8 type;
+ __u8 reserved;
+ __u16 len;
+ union {
+ __u8 id[8];
+ struct w1_mst {
+ __u32 id;
+ __u32 res;
+ } mst;
+ } id;
+ __u8 data[0];
+};
+
+#define W1_CMD_READ 0x0
+#define W1_CMD_WRITE 0x1
+#define W1_CMD_SEARCH 0x2
+#define W1_CMD_ALARM_SEARCH 0x3
+
+struct w1_netlink_cmd
+{
+ __u8 cmd;
+ __u8 res;
+ __u16 len;
+ __u8 data[0];
+};
+
+#ifdef __KERNEL__
+
+void w1_netlink_send(struct w1_master *, struct w1_netlink_msg *);
+int w1_init_netlink(void);
+void w1_fini_netlink(void);
+
+#endif /* __KERNEL__ */
+#endif /* __W1_NETLINK_H */
OpenPOWER on IntegriCloud