summaryrefslogtreecommitdiffstats
path: root/drivers/w1
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/w1')
-rw-r--r--drivers/w1/masters/Kconfig2
-rw-r--r--drivers/w1/masters/ds1wm.c333
-rw-r--r--drivers/w1/slaves/Kconfig20
-rw-r--r--drivers/w1/slaves/Makefile2
-rw-r--r--drivers/w1/slaves/w1_ds2408.c402
-rw-r--r--drivers/w1/slaves/w1_ds2780.c217
-rw-r--r--drivers/w1/slaves/w1_ds2780.h129
-rw-r--r--drivers/w1/w1.c12
-rw-r--r--drivers/w1/w1.h6
-rw-r--r--drivers/w1/w1_family.h2
-rw-r--r--drivers/w1/w1_io.c26
-rw-r--r--drivers/w1/w1_netlink.c5
12 files changed, 1046 insertions, 110 deletions
diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig
index 7c608c5ccf8..00d615d7aa2 100644
--- a/drivers/w1/masters/Kconfig
+++ b/drivers/w1/masters/Kconfig
@@ -42,7 +42,7 @@ config W1_MASTER_MXC
config W1_MASTER_DS1WM
tristate "Maxim DS1WM 1-wire busmaster"
- depends on W1 && ARM && HAVE_CLK
+ depends on W1
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
diff --git a/drivers/w1/masters/ds1wm.c b/drivers/w1/masters/ds1wm.c
index 2f4fa02744a..ad57593d224 100644
--- a/drivers/w1/masters/ds1wm.c
+++ b/drivers/w1/masters/ds1wm.c
@@ -33,6 +33,7 @@
#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_CNTRL 0x05 /* R/W master control register (not used yet) */
#define DS1WM_CMD_1W_RESET (1 << 0) /* force reset on 1-wire bus */
#define DS1WM_CMD_SRA (1 << 1) /* enable Search ROM accelerator mode */
@@ -56,6 +57,7 @@
#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_INTEN_NOT_IAS (~DS1WM_INTEN_IAS) /* all but INTR active state */
#define DS1WM_TIMEOUT (HZ * 5)
@@ -63,41 +65,50 @@ 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 },
+ { 1000000, 0x80 },
+ { 2000000, 0x84 },
+ { 3000000, 0x81 },
+ { 4000000, 0x88 },
+ { 5000000, 0x82 },
+ { 6000000, 0x85 },
+ { 7000000, 0x83 },
+ { 8000000, 0x8c },
+ { 10000000, 0x86 },
+ { 12000000, 0x89 },
+ { 14000000, 0x87 },
+ { 16000000, 0x90 },
+ { 20000000, 0x8a },
+ { 24000000, 0x8d },
+ { 28000000, 0x8b },
+ { 32000000, 0x94 },
+ { 40000000, 0x8e },
+ { 48000000, 0x91 },
+ { 56000000, 0x8f },
+ { 64000000, 0x98 },
+ { 80000000, 0x92 },
+ { 96000000, 0x95 },
+ { 112000000, 0x93 },
+ { 128000000, 0x9c },
+/* you can continue this table, consult the OPERATION - CLOCK DIVISOR
+ section of the ds1wm spec sheet. */
};
struct ds1wm_data {
- void __iomem *map;
- int bus_shift; /* # of shifts to calc register offsets */
+ void __iomem *map;
+ int bus_shift; /* # of shifts to calc register offsets */
struct platform_device *pdev;
- const struct mfd_cell *cell;
- int irq;
- int active_high;
- int slave_present;
- void *reset_complete;
- void *read_complete;
- void *write_complete;
- u8 read_byte; /* last byte received */
+ const struct mfd_cell *cell;
+ int irq;
+ int slave_present;
+ void *reset_complete;
+ void *read_complete;
+ void *write_complete;
+ int read_error;
+ /* last byte received */
+ u8 read_byte;
+ /* byte to write that makes all intr disabled, */
+ /* considering active_state (IAS) (optimization) */
+ u8 int_en_reg_none;
};
static inline void ds1wm_write_register(struct ds1wm_data *ds1wm_data, u32 reg,
@@ -115,23 +126,39 @@ static inline u8 ds1wm_read_register(struct ds1wm_data *ds1wm_data, u32 reg)
static irqreturn_t ds1wm_isr(int isr, void *data)
{
struct ds1wm_data *ds1wm_data = data;
- u8 intr = ds1wm_read_register(ds1wm_data, DS1WM_INT);
+ u8 intr;
+ u8 inten = ds1wm_read_register(ds1wm_data, DS1WM_INT_EN);
+ /* if no bits are set in int enable register (except the IAS)
+ than go no further, reading the regs below has side effects */
+ if (!(inten & DS1WM_INTEN_NOT_IAS))
+ return IRQ_NONE;
- ds1wm_data->slave_present = (intr & DS1WM_INT_PDR) ? 0 : 1;
+ ds1wm_write_register(ds1wm_data,
+ DS1WM_INT_EN, ds1wm_data->int_en_reg_none);
- if ((intr & DS1WM_INT_PD) && ds1wm_data->reset_complete)
- complete(ds1wm_data->reset_complete);
+ /* this read action clears the INTR and certain flags in ds1wm */
+ intr = ds1wm_read_register(ds1wm_data, DS1WM_INT);
- if ((intr & DS1WM_INT_TSRE) && ds1wm_data->write_complete)
- complete(ds1wm_data->write_complete);
+ ds1wm_data->slave_present = (intr & DS1WM_INT_PDR) ? 0 : 1;
+ if ((intr & DS1WM_INT_TSRE) && ds1wm_data->write_complete) {
+ inten &= ~DS1WM_INTEN_ETMT;
+ complete(ds1wm_data->write_complete);
+ }
if (intr & DS1WM_INT_RBF) {
+ /* this read clears the RBF flag */
ds1wm_data->read_byte = ds1wm_read_register(ds1wm_data,
- DS1WM_DATA);
+ DS1WM_DATA);
+ inten &= ~DS1WM_INTEN_ERBF;
if (ds1wm_data->read_complete)
complete(ds1wm_data->read_complete);
}
+ if ((intr & DS1WM_INT_PD) && ds1wm_data->reset_complete) {
+ inten &= ~DS1WM_INTEN_EPD;
+ complete(ds1wm_data->reset_complete);
+ }
+ ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, inten);
return IRQ_HANDLED;
}
@@ -142,33 +169,19 @@ static int ds1wm_reset(struct ds1wm_data *ds1wm_data)
ds1wm_data->reset_complete = &reset_done;
+ /* enable Presence detect only */
ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, DS1WM_INTEN_EPD |
- (ds1wm_data->active_high ? DS1WM_INTEN_IAS : 0));
+ ds1wm_data->int_en_reg_none);
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");
+ dev_err(&ds1wm_data->pdev->dev, "reset failed, timed out\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;
@@ -179,26 +192,47 @@ static int ds1wm_reset(struct ds1wm_data *ds1wm_data)
static int ds1wm_write(struct ds1wm_data *ds1wm_data, u8 data)
{
+ unsigned long timeleft;
DECLARE_COMPLETION_ONSTACK(write_done);
ds1wm_data->write_complete = &write_done;
+ ds1wm_write_register(ds1wm_data, DS1WM_INT_EN,
+ ds1wm_data->int_en_reg_none | DS1WM_INTEN_ETMT);
+
ds1wm_write_register(ds1wm_data, DS1WM_DATA, data);
- wait_for_completion_timeout(&write_done, DS1WM_TIMEOUT);
+ timeleft = wait_for_completion_timeout(&write_done, DS1WM_TIMEOUT);
+
ds1wm_data->write_complete = NULL;
+ if (!timeleft) {
+ dev_err(&ds1wm_data->pdev->dev, "write failed, timed out\n");
+ return -ETIMEDOUT;
+ }
return 0;
}
-static int ds1wm_read(struct ds1wm_data *ds1wm_data, unsigned char write_data)
+static u8 ds1wm_read(struct ds1wm_data *ds1wm_data, unsigned char write_data)
{
+ unsigned long timeleft;
+ u8 intEnable = DS1WM_INTEN_ERBF | ds1wm_data->int_en_reg_none;
DECLARE_COMPLETION_ONSTACK(read_done);
+
+ ds1wm_read_register(ds1wm_data, DS1WM_DATA);
+
ds1wm_data->read_complete = &read_done;
+ ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, intEnable);
- ds1wm_write(ds1wm_data, write_data);
- wait_for_completion_timeout(&read_done, DS1WM_TIMEOUT);
- ds1wm_data->read_complete = NULL;
+ ds1wm_write_register(ds1wm_data, DS1WM_DATA, write_data);
+ timeleft = wait_for_completion_timeout(&read_done, DS1WM_TIMEOUT);
+ ds1wm_data->read_complete = NULL;
+ if (!timeleft) {
+ dev_err(&ds1wm_data->pdev->dev, "read failed, timed out\n");
+ ds1wm_data->read_error = -ETIMEDOUT;
+ return 0xFF;
+ }
+ ds1wm_data->read_error = 0;
return ds1wm_data->read_byte;
}
@@ -206,8 +240,8 @@ static int ds1wm_find_divisor(int gclk)
{
int i;
- for (i = 0; i < ARRAY_SIZE(freq); i++)
- if (gclk <= freq[i].freq)
+ for (i = ARRAY_SIZE(freq)-1; i >= 0; --i)
+ if (gclk >= freq[i].freq)
return freq[i].divisor;
return 0;
@@ -216,12 +250,14 @@ static int ds1wm_find_divisor(int gclk)
static void ds1wm_up(struct ds1wm_data *ds1wm_data)
{
int divisor;
- struct ds1wm_driver_data *plat = mfd_get_data(ds1wm_data->pdev);
+ struct ds1wm_driver_data *plat = ds1wm_data->pdev->dev.platform_data;
if (ds1wm_data->cell->enable)
ds1wm_data->cell->enable(ds1wm_data->pdev);
divisor = ds1wm_find_divisor(plat->clock_rate);
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "found divisor 0x%x for clock %d\n", divisor, plat->clock_rate);
if (divisor == 0) {
dev_err(&ds1wm_data->pdev->dev,
"no suitable divisor for %dHz clock\n",
@@ -242,7 +278,7 @@ static void ds1wm_down(struct ds1wm_data *ds1wm_data)
/* Disable interrupts. */
ds1wm_write_register(ds1wm_data, DS1WM_INT_EN,
- ds1wm_data->active_high ? DS1WM_INTEN_IAS : 0);
+ ds1wm_data->int_en_reg_none);
if (ds1wm_data->cell->disable)
ds1wm_data->cell->disable(ds1wm_data->pdev);
@@ -279,41 +315,121 @@ static void ds1wm_search(void *data, struct w1_master *master_dev,
{
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);
+ int ms_discrep_bit = -1;
+ u64 r = 0; /* holds the progress of the search */
+ u64 r_prime, d;
+ unsigned slaves_found = 0;
+ unsigned int pass = 0;
+
+ dev_dbg(&ds1wm_data->pdev->dev, "search begin\n");
+ while (true) {
+ ++pass;
+ if (pass > 100) {
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "too many attempts (100), search aborted\n");
+ return;
+ }
+
+ if (ds1wm_reset(ds1wm_data)) {
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "pass: %d reset error (or no slaves)\n", pass);
+ break;
+ }
+
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "pass: %d r : %0#18llx writing SEARCH_ROM\n", pass, r);
+ ds1wm_write(ds1wm_data, search_type);
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "pass: %d entering ASM\n", pass);
+ ds1wm_write_register(ds1wm_data, DS1WM_CMD, DS1WM_CMD_SRA);
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "pass: %d begining nibble loop\n", pass);
+
+ r_prime = 0;
+ d = 0;
+ /* we work one nibble at a time */
+ /* each nibble is interleaved to form a byte */
+ for (i = 0; i < 16; i++) {
+
+ unsigned char resp, _r, _r_prime, _d;
+
+ _r = (r >> (4*i)) & 0xf;
+ _r = ((_r & 0x1) << 1) |
+ ((_r & 0x2) << 2) |
+ ((_r & 0x4) << 3) |
+ ((_r & 0x8) << 4);
+
+ /* writes _r, then reads back: */
+ resp = ds1wm_read(ds1wm_data, _r);
+
+ if (ds1wm_data->read_error) {
+ dev_err(&ds1wm_data->pdev->dev,
+ "pass: %d nibble: %d read error\n", pass, i);
+ break;
+ }
+
+ _r_prime = ((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);
+
+ r_prime |= (unsigned long long) _r_prime << (i * 4);
+ d |= (unsigned long long) _d << (i * 4);
+
+ }
+ if (ds1wm_data->read_error) {
+ dev_err(&ds1wm_data->pdev->dev,
+ "pass: %d read error, retrying\n", pass);
+ break;
+ }
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "pass: %d r\': %0#18llx d:%0#18llx\n",
+ pass, r_prime, d);
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "pass: %d nibble loop complete, exiting ASM\n", pass);
+ ds1wm_write_register(ds1wm_data, DS1WM_CMD, ~DS1WM_CMD_SRA);
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "pass: %d resetting bus\n", pass);
+ ds1wm_reset(ds1wm_data);
+ if ((r_prime & ((u64)1 << 63)) && (d & ((u64)1 << 63))) {
+ dev_err(&ds1wm_data->pdev->dev,
+ "pass: %d bus error, retrying\n", pass);
+ continue; /* start over */
+ }
+
+
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "pass: %d found %0#18llx\n", pass, r_prime);
+ slave_found(master_dev, r_prime);
+ ++slaves_found;
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "pass: %d complete, preparing next pass\n", pass);
+
+ /* any discrepency found which we already choose the
+ '1' branch is now is now irrelevant we reveal the
+ next branch with this: */
+ d &= ~r;
+ /* find last bit set, i.e. the most signif. bit set */
+ ms_discrep_bit = fls64(d) - 1;
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "pass: %d new d:%0#18llx MS discrep bit:%d\n",
+ pass, d, ms_discrep_bit);
+
+ /* prev_ms_discrep_bit = ms_discrep_bit;
+ prepare for next ROM search: */
+ if (ms_discrep_bit == -1)
+ break;
+
+ r = (r & ~(~0ull << (ms_discrep_bit))) | 1 << ms_discrep_bit;
+ } /* end while true */
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "pass: %d total: %d search done ms d bit pos: %d\n", pass,
+ slaves_found, ms_discrep_bit);
}
/* --------------------------------------------------------------------- */
@@ -351,13 +467,21 @@ static int ds1wm_probe(struct platform_device *pdev)
ret = -ENOMEM;
goto err0;
}
- plat = mfd_get_data(pdev);
/* calculate bus shift from mem resource */
ds1wm_data->bus_shift = resource_size(res) >> 3;
ds1wm_data->pdev = pdev;
ds1wm_data->cell = mfd_get_cell(pdev);
+ if (!ds1wm_data->cell) {
+ ret = -ENODEV;
+ goto err1;
+ }
+ plat = pdev->dev.platform_data;
+ if (!plat) {
+ ret = -ENODEV;
+ goto err1;
+ }
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
@@ -365,15 +489,15 @@ static int ds1wm_probe(struct platform_device *pdev)
goto err1;
}
ds1wm_data->irq = res->start;
- ds1wm_data->active_high = plat->active_high;
+ ds1wm_data->int_en_reg_none = (plat->active_high ? DS1WM_INTEN_IAS : 0);
if (res->flags & IORESOURCE_IRQ_HIGHEDGE)
irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_RISING);
if (res->flags & IORESOURCE_IRQ_LOWEDGE)
irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_FALLING);
- ret = request_irq(ds1wm_data->irq, ds1wm_isr, IRQF_DISABLED,
- "ds1wm", ds1wm_data);
+ ret = request_irq(ds1wm_data->irq, ds1wm_isr,
+ IRQF_DISABLED | IRQF_SHARED, "ds1wm", ds1wm_data);
if (ret)
goto err1;
@@ -460,5 +584,6 @@ module_exit(ds1wm_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>, "
- "Matt Reimer <mreimer@vpop.net>");
+ "Matt Reimer <mreimer@vpop.net>,"
+ "Jean-Francois Dagenais <dagenaisj@sonatest.com>");
MODULE_DESCRIPTION("DS1WM w1 busmaster driver");
diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig
index f0c909625bd..d0cb01b4201 100644
--- a/drivers/w1/slaves/Kconfig
+++ b/drivers/w1/slaves/Kconfig
@@ -16,6 +16,13 @@ config W1_SLAVE_SMEM
Say Y here if you want to connect 1-wire
simple 64bit memory rom(ds2401/ds2411/ds1990*) to your wire.
+config W1_SLAVE_DS2408
+ tristate "8-Channel Addressable Switch (IO Expander) 0x29 family support (DS2408)"
+ help
+ Say Y here if you want to use a 1-wire
+
+ DS2408 8-Channel Addressable Switch device support
+
config W1_SLAVE_DS2423
tristate "Counter 1-wire device (DS2423)"
select CRC16
@@ -61,6 +68,19 @@ config W1_SLAVE_DS2760
If you are unsure, say N.
+config W1_SLAVE_DS2780
+ tristate "Dallas 2780 battery monitor chip"
+ depends on W1
+ help
+ If you enable this you will have the DS2780 battery monitor
+ chip support.
+
+ The battery monitor chip is used in many batteries/devices
+ as the one who is responsible for charging/discharging/monitoring
+ Li+ batteries.
+
+ If you are unsure, say N.
+
config W1_SLAVE_BQ27000
tristate "BQ27000 slave support"
depends on W1
diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile
index 3c76350a24f..1f31e9fb0b2 100644
--- a/drivers/w1/slaves/Makefile
+++ b/drivers/w1/slaves/Makefile
@@ -4,8 +4,10 @@
obj-$(CONFIG_W1_SLAVE_THERM) += w1_therm.o
obj-$(CONFIG_W1_SLAVE_SMEM) += w1_smem.o
+obj-$(CONFIG_W1_SLAVE_DS2408) += w1_ds2408.o
obj-$(CONFIG_W1_SLAVE_DS2423) += w1_ds2423.o
obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o
obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o
obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o
+obj-$(CONFIG_W1_SLAVE_DS2780) += w1_ds2780.o
obj-$(CONFIG_W1_SLAVE_BQ27000) += w1_bq27000.o
diff --git a/drivers/w1/slaves/w1_ds2408.c b/drivers/w1/slaves/w1_ds2408.c
new file mode 100644
index 00000000000..c37781899d9
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2408.c
@@ -0,0 +1,402 @@
+/*
+ * w1_ds2408.c - w1 family 29 (DS2408) driver
+ *
+ * Copyright (c) 2010 Jean-Francois Dagenais <dagenaisj@sonatest.com>
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include "../w1.h"
+#include "../w1_int.h"
+#include "../w1_family.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jean-Francois Dagenais <dagenaisj@sonatest.com>");
+MODULE_DESCRIPTION("w1 family 29 driver for DS2408 8 Pin IO");
+
+
+#define W1_F29_RETRIES 3
+
+#define W1_F29_REG_LOGIG_STATE 0x88 /* R */
+#define W1_F29_REG_OUTPUT_LATCH_STATE 0x89 /* R */
+#define W1_F29_REG_ACTIVITY_LATCH_STATE 0x8A /* R */
+#define W1_F29_REG_COND_SEARCH_SELECT_MASK 0x8B /* RW */
+#define W1_F29_REG_COND_SEARCH_POL_SELECT 0x8C /* RW */
+#define W1_F29_REG_CONTROL_AND_STATUS 0x8D /* RW */
+
+#define W1_F29_FUNC_READ_PIO_REGS 0xF0
+#define W1_F29_FUNC_CHANN_ACCESS_READ 0xF5
+#define W1_F29_FUNC_CHANN_ACCESS_WRITE 0x5A
+/* also used to write the control/status reg (0x8D): */
+#define W1_F29_FUNC_WRITE_COND_SEARCH_REG 0xCC
+#define W1_F29_FUNC_RESET_ACTIVITY_LATCHES 0xC3
+
+#define W1_F29_SUCCESS_CONFIRM_BYTE 0xAA
+
+static int _read_reg(struct w1_slave *sl, u8 address, unsigned char* buf)
+{
+ u8 wrbuf[3];
+ dev_dbg(&sl->dev,
+ "Reading with slave: %p, reg addr: %0#4x, buff addr: %p",
+ sl, (unsigned int)address, buf);
+
+ if (!buf)
+ return -EINVAL;
+
+ mutex_lock(&sl->master->mutex);
+ dev_dbg(&sl->dev, "mutex locked");
+
+ if (w1_reset_select_slave(sl)) {
+ mutex_unlock(&sl->master->mutex);
+ return -EIO;
+ }
+
+ wrbuf[0] = W1_F29_FUNC_READ_PIO_REGS;
+ wrbuf[1] = address;
+ wrbuf[2] = 0;
+ w1_write_block(sl->master, wrbuf, 3);
+ *buf = w1_read_8(sl->master);
+
+ mutex_unlock(&sl->master->mutex);
+ dev_dbg(&sl->dev, "mutex unlocked");
+ return 1;
+}
+
+static ssize_t w1_f29_read_state(
+ struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ dev_dbg(&kobj_to_w1_slave(kobj)->dev,
+ "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p",
+ bin_attr->attr.name, kobj, (unsigned int)off, count, buf);
+ if (count != 1 || off != 0)
+ return -EFAULT;
+ return _read_reg(kobj_to_w1_slave(kobj), W1_F29_REG_LOGIG_STATE, buf);
+}
+
+static ssize_t w1_f29_read_output(
+ struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ dev_dbg(&kobj_to_w1_slave(kobj)->dev,
+ "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p",
+ bin_attr->attr.name, kobj, (unsigned int)off, count, buf);
+ if (count != 1 || off != 0)
+ return -EFAULT;
+ return _read_reg(kobj_to_w1_slave(kobj),
+ W1_F29_REG_OUTPUT_LATCH_STATE, buf);
+}
+
+static ssize_t w1_f29_read_activity(
+ struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ dev_dbg(&kobj_to_w1_slave(kobj)->dev,
+ "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p",
+ bin_attr->attr.name, kobj, (unsigned int)off, count, buf);
+ if (count != 1 || off != 0)
+ return -EFAULT;
+ return _read_reg(kobj_to_w1_slave(kobj),
+ W1_F29_REG_ACTIVITY_LATCH_STATE, buf);
+}
+
+static ssize_t w1_f29_read_cond_search_mask(
+ struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ dev_dbg(&kobj_to_w1_slave(kobj)->dev,
+ "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p",
+ bin_attr->attr.name, kobj, (unsigned int)off, count, buf);
+ if (count != 1 || off != 0)
+ return -EFAULT;
+ return _read_reg(kobj_to_w1_slave(kobj),
+ W1_F29_REG_COND_SEARCH_SELECT_MASK, buf);
+}
+
+static ssize_t w1_f29_read_cond_search_polarity(
+ struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ if (count != 1 || off != 0)
+ return -EFAULT;
+ return _read_reg(kobj_to_w1_slave(kobj),
+ W1_F29_REG_COND_SEARCH_POL_SELECT, buf);
+}
+
+static ssize_t w1_f29_read_status_control(
+ struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ if (count != 1 || off != 0)
+ return -EFAULT;
+ return _read_reg(kobj_to_w1_slave(kobj),
+ W1_F29_REG_CONTROL_AND_STATUS, buf);
+}
+
+
+
+
+static ssize_t w1_f29_write_output(
+ struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ u8 w1_buf[3];
+ u8 readBack;
+ unsigned int retries = W1_F29_RETRIES;
+
+ if (count != 1 || off != 0)
+ return -EFAULT;
+
+ dev_dbg(&sl->dev, "locking mutex for write_output");
+ mutex_lock(&sl->master->mutex);
+ dev_dbg(&sl->dev, "mutex locked");
+
+ if (w1_reset_select_slave(sl))
+ goto error;
+
+ while (retries--) {
+ w1_buf[0] = W1_F29_FUNC_CHANN_ACCESS_WRITE;
+ w1_buf[1] = *buf;
+ w1_buf[2] = ~(*buf);
+ w1_write_block(sl->master, w1_buf, 3);
+
+ readBack = w1_read_8(sl->master);
+ /* here the master could read another byte which
+ would be the PIO reg (the actual pin logic state)
+ since in this driver we don't know which pins are
+ in and outs, there's no value to read the state and
+ compare. with (*buf) so end this command abruptly: */
+ if (w1_reset_resume_command(sl->master))
+ goto error;
+
+ if (readBack != 0xAA) {
+ /* try again, the slave is ready for a command */
+ continue;
+ }
+
+ /* go read back the output latches */
+ /* (the direct effect of the write above) */
+ w1_buf[0] = W1_F29_FUNC_READ_PIO_REGS;
+ w1_buf[1] = W1_F29_REG_OUTPUT_LATCH_STATE;
+ w1_buf[2] = 0;
+ w1_write_block(sl->master, w1_buf, 3);
+ /* read the result of the READ_PIO_REGS command */
+ if (w1_read_8(sl->master) == *buf) {
+ /* success! */
+ mutex_unlock(&sl->master->mutex);
+ dev_dbg(&sl->dev,
+ "mutex unlocked, retries:%d", retries);
+ return 1;
+ }
+ }
+error:
+ mutex_unlock(&sl->master->mutex);
+ dev_dbg(&sl->dev, "mutex unlocked in error, retries:%d", retries);
+
+ return -EIO;
+}
+
+
+/**
+ * Writing to the activity file resets the activity latches.
+ */
+static ssize_t w1_f29_write_activity(
+ struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ unsigned int retries = W1_F29_RETRIES;
+
+ if (count != 1 || off != 0)
+ return -EFAULT;
+
+ mutex_lock(&sl->master->mutex);
+
+ if (w1_reset_select_slave(sl))
+ goto error;
+
+ while (retries--) {
+ w1_write_8(sl->master, W1_F29_FUNC_RESET_ACTIVITY_LATCHES);
+ if (w1_read_8(sl->master) == W1_F29_SUCCESS_CONFIRM_BYTE) {
+ mutex_unlock(&sl->master->mutex);
+ return 1;
+ }
+ if (w1_reset_resume_command(sl->master))
+ goto error;
+ }
+
+error:
+ mutex_unlock(&sl->master->mutex);
+ return -EIO;
+}
+
+static ssize_t w1_f29_write_status_control(
+ struct file *filp,
+ struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf,
+ loff_t off,
+ size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ u8 w1_buf[4];
+ unsigned int retries = W1_F29_RETRIES;
+
+ if (count != 1 || off != 0)
+ return -EFAULT;
+
+ mutex_lock(&sl->master->mutex);
+
+ if (w1_reset_select_slave(sl))
+ goto error;
+
+ while (retries--) {
+ w1_buf[0] = W1_F29_FUNC_WRITE_COND_SEARCH_REG;
+ w1_buf[1] = W1_F29_REG_CONTROL_AND_STATUS;
+ w1_buf[2] = 0;
+ w1_buf[3] = *buf;
+
+ w1_write_block(sl->master, w1_buf, 4);
+ if (w1_reset_resume_command(sl->master))
+ goto error;
+
+ w1_buf[0] = W1_F29_FUNC_READ_PIO_REGS;
+ w1_buf[1] = W1_F29_REG_CONTROL_AND_STATUS;
+ w1_buf[2] = 0;
+
+ w1_write_block(sl->master, w1_buf, 3);
+ if (w1_read_8(sl->master) == *buf) {
+ /* success! */
+ mutex_unlock(&sl->master->mutex);
+ return 1;
+ }
+ }
+error:
+ mutex_unlock(&sl->master->mutex);
+
+ return -EIO;
+}
+
+
+
+#define NB_SYSFS_BIN_FILES 6
+static struct bin_attribute w1_f29_sysfs_bin_files[NB_SYSFS_BIN_FILES] = {
+ {
+ .attr = {
+ .name = "state",
+ .mode = S_IRUGO,
+ },
+ .size = 1,
+ .read = w1_f29_read_state,
+ },
+ {
+ .attr = {
+ .name = "output",
+ .mode = S_IRUGO | S_IWUSR | S_IWGRP,
+ },
+ .size = 1,
+ .read = w1_f29_read_output,
+ .write = w1_f29_write_output,
+ },
+ {
+ .attr = {
+ .name = "activity",
+ .mode = S_IRUGO,
+ },
+ .size = 1,
+ .read = w1_f29_read_activity,
+ .write = w1_f29_write_activity,
+ },
+ {
+ .attr = {
+ .name = "cond_search_mask",
+ .mode = S_IRUGO,
+ },
+ .size = 1,
+ .read = w1_f29_read_cond_search_mask,
+ .write = 0,
+ },
+ {
+ .attr = {
+ .name = "cond_search_polarity",
+ .mode = S_IRUGO,
+ },
+ .size = 1,
+ .read = w1_f29_read_cond_search_polarity,
+ .write = 0,
+ },
+ {
+ .attr = {
+ .name = "status_control",
+ .mode = S_IRUGO | S_IWUSR | S_IWGRP,
+ },
+ .size = 1,
+ .read = w1_f29_read_status_control,
+ .write = w1_f29_write_status_control,
+ }
+};
+
+static int w1_f29_add_slave(struct w1_slave *sl)
+{
+ int err = 0;
+ int i;
+
+ for (i = 0; i < NB_SYSFS_BIN_FILES && !err; ++i)
+ err = sysfs_create_bin_file(
+ &sl->dev.kobj,
+ &(w1_f29_sysfs_bin_files[i]));
+ if (err)
+ while (--i >= 0)
+ sysfs_remove_bin_file(&sl->dev.kobj,
+ &(w1_f29_sysfs_bin_files[i]));
+ return err;
+}
+
+static void w1_f29_remove_slave(struct w1_slave *sl)
+{
+ int i;
+ for (i = NB_SYSFS_BIN_FILES; i <= 0; --i)
+ sysfs_remove_bin_file(&sl->dev.kobj,
+ &(w1_f29_sysfs_bin_files[i]));
+}
+
+static struct w1_family_ops w1_f29_fops = {
+ .add_slave = w1_f29_add_slave,
+ .remove_slave = w1_f29_remove_slave,
+};
+
+static struct w1_family w1_family_29 = {
+ .fid = W1_FAMILY_DS2408,
+ .fops = &w1_f29_fops,
+};
+
+static int __init w1_f29_init(void)
+{
+ return w1_register_family(&w1_family_29);
+}
+
+static void __exit w1_f29_exit(void)
+{
+ w1_unregister_family(&w1_family_29);
+}
+
+module_init(w1_f29_init);
+module_exit(w1_f29_exit);
diff --git a/drivers/w1/slaves/w1_ds2780.c b/drivers/w1/slaves/w1_ds2780.c
new file mode 100644
index 00000000000..274c8f38303
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2780.c
@@ -0,0 +1,217 @@
+/*
+ * 1-Wire implementation for the ds2780 chip
+ *
+ * Copyright (C) 2010 Indesign, LLC
+ *
+ * Author: Clifton Barnes <cabarnes@indesign-llc.com>
+ *
+ * Based on w1-ds2760 driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/idr.h>
+
+#include "../w1.h"
+#include "../w1_int.h"
+#include "../w1_family.h"
+#include "w1_ds2780.h"
+
+int w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count,
+ int io)
+{
+ struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&sl->master->mutex);
+
+ if (addr > DS2780_DATA_SIZE || addr < 0) {
+ count = 0;
+ goto out;
+ }
+ count = min_t(int, count, DS2780_DATA_SIZE - addr);
+
+ if (w1_reset_select_slave(sl) == 0) {
+ if (io) {
+ w1_write_8(sl->master, W1_DS2780_WRITE_DATA);
+ w1_write_8(sl->master, addr);
+ w1_write_block(sl->master, buf, count);
+ /* XXX w1_write_block returns void, not n_written */
+ } else {
+ w1_write_8(sl->master, W1_DS2780_READ_DATA);
+ w1_write_8(sl->master, addr);
+ count = w1_read_block(sl->master, buf, count);
+ }
+ }
+
+out:
+ mutex_unlock(&sl->master->mutex);
+
+ return count;
+}
+EXPORT_SYMBOL(w1_ds2780_io);
+
+int w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd)
+{
+ struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+
+ if (!dev)
+ return -EINVAL;
+
+ mutex_lock(&sl->master->mutex);
+
+ if (w1_reset_select_slave(sl) == 0) {
+ w1_write_8(sl->master, cmd);
+ w1_write_8(sl->master, addr);
+ }
+
+ mutex_unlock(&sl->master->mutex);
+ return 0;
+}
+EXPORT_SYMBOL(w1_ds2780_eeprom_cmd);
+
+static ssize_t w1_ds2780_read_bin(struct file *filp,
+ struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ return w1_ds2780_io(dev, buf, off, count, 0);
+}
+
+static struct bin_attribute w1_ds2780_bin_attr = {
+ .attr = {
+ .name = "w1_slave",
+ .mode = S_IRUGO,
+ },
+ .size = DS2780_DATA_SIZE,
+ .read = w1_ds2780_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_ds2780_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("ds2780-battery", id);
+ if (!pdev) {
+ ret = -ENOMEM;
+ goto pdev_alloc_failed;
+ }
+ pdev->dev.parent = &sl->dev;
+
+ ret = platform_device_add(pdev);
+ if (ret)
+ goto pdev_add_failed;
+
+ ret = sysfs_create_bin_file(&sl->dev.kobj, &w1_ds2780_bin_attr);
+ if (ret)
+ goto bin_attr_failed;
+
+ dev_set_drvdata(&sl->dev, pdev);
+
+ return 0;
+
+bin_attr_failed:
+pdev_add_failed:
+ platform_device_unregister(pdev);
+pdev_alloc_failed:
+ release_bat_id(id);
+noid:
+ return ret;
+}
+
+static void w1_ds2780_remove_slave(struct w1_slave *sl)
+{
+ struct platform_device *pdev = dev_get_drvdata(&sl->dev);
+ int id = pdev->id;
+
+ platform_device_unregister(pdev);
+ release_bat_id(id);
+ sysfs_remove_bin_file(&sl->dev.kobj, &w1_ds2780_bin_attr);
+}
+
+static struct w1_family_ops w1_ds2780_fops = {
+ .add_slave = w1_ds2780_add_slave,
+ .remove_slave = w1_ds2780_remove_slave,
+};
+
+static struct w1_family w1_ds2780_family = {
+ .fid = W1_FAMILY_DS2780,
+ .fops = &w1_ds2780_fops,
+};
+
+static int __init w1_ds2780_init(void)
+{
+ idr_init(&bat_idr);
+ return w1_register_family(&w1_ds2780_family);
+}
+
+static void __exit w1_ds2780_exit(void)
+{
+ w1_unregister_family(&w1_ds2780_family);
+ idr_destroy(&bat_idr);
+}
+
+module_init(w1_ds2780_init);
+module_exit(w1_ds2780_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Clifton Barnes <cabarnes@indesign-llc.com>");
+MODULE_DESCRIPTION("1-wire Driver for Maxim/Dallas DS2780 Stand-Alone Fuel Gauge IC");
diff --git a/drivers/w1/slaves/w1_ds2780.h b/drivers/w1/slaves/w1_ds2780.h
new file mode 100644
index 00000000000..a1fba79eb1b
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2780.h
@@ -0,0 +1,129 @@
+/*
+ * 1-Wire implementation for the ds2780 chip
+ *
+ * Copyright (C) 2010 Indesign, LLC
+ *
+ * Author: Clifton Barnes <cabarnes@indesign-llc.com>
+ *
+ * Based on w1-ds2760 driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef _W1_DS2780_H
+#define _W1_DS2780_H
+
+/* Function commands */
+#define W1_DS2780_READ_DATA 0x69
+#define W1_DS2780_WRITE_DATA 0x6C
+#define W1_DS2780_COPY_DATA 0x48
+#define W1_DS2780_RECALL_DATA 0xB8
+#define W1_DS2780_LOCK 0x6A
+
+/* Register map */
+/* Register 0x00 Reserved */
+#define DS2780_STATUS_REG 0x01
+#define DS2780_RAAC_MSB_REG 0x02
+#define DS2780_RAAC_LSB_REG 0x03
+#define DS2780_RSAC_MSB_REG 0x04
+#define DS2780_RSAC_LSB_REG 0x05
+#define DS2780_RARC_REG 0x06
+#define DS2780_RSRC_REG 0x07
+#define DS2780_IAVG_MSB_REG 0x08
+#define DS2780_IAVG_LSB_REG 0x09
+#define DS2780_TEMP_MSB_REG 0x0A
+#define DS2780_TEMP_LSB_REG 0x0B
+#define DS2780_VOLT_MSB_REG 0x0C
+#define DS2780_VOLT_LSB_REG 0x0D
+#define DS2780_CURRENT_MSB_REG 0x0E
+#define DS2780_CURRENT_LSB_REG 0x0F
+#define DS2780_ACR_MSB_REG 0x10
+#define DS2780_ACR_LSB_REG 0x11
+#define DS2780_ACRL_MSB_REG 0x12
+#define DS2780_ACRL_LSB_REG 0x13
+#define DS2780_AS_REG 0x14
+#define DS2780_SFR_REG 0x15
+#define DS2780_FULL_MSB_REG 0x16
+#define DS2780_FULL_LSB_REG 0x17
+#define DS2780_AE_MSB_REG 0x18
+#define DS2780_AE_LSB_REG 0x19
+#define DS2780_SE_MSB_REG 0x1A
+#define DS2780_SE_LSB_REG 0x1B
+/* Register 0x1C - 0x1E Reserved */
+#define DS2780_EEPROM_REG 0x1F
+#define DS2780_EEPROM_BLOCK0_START 0x20
+/* Register 0x20 - 0x2F User EEPROM */
+#define DS2780_EEPROM_BLOCK0_END 0x2F
+/* Register 0x30 - 0x5F Reserved */
+#define DS2780_EEPROM_BLOCK1_START 0x60
+#define DS2780_CONTROL_REG 0x60
+#define DS2780_AB_REG 0x61
+#define DS2780_AC_MSB_REG 0x62
+#define DS2780_AC_LSB_REG 0x63
+#define DS2780_VCHG_REG 0x64
+#define DS2780_IMIN_REG 0x65
+#define DS2780_VAE_REG 0x66
+#define DS2780_IAE_REG 0x67
+#define DS2780_AE_40_REG 0x68
+#define DS2780_RSNSP_REG 0x69
+#define DS2780_FULL_40_MSB_REG 0x6A
+#define DS2780_FULL_40_LSB_REG 0x6B
+#define DS2780_FULL_3040_SLOPE_REG 0x6C
+#define DS2780_FULL_2030_SLOPE_REG 0x6D
+#define DS2780_FULL_1020_SLOPE_REG 0x6E
+#define DS2780_FULL_0010_SLOPE_REG 0x6F
+#define DS2780_AE_3040_SLOPE_REG 0x70
+#define DS2780_AE_2030_SLOPE_REG 0x71
+#define DS2780_AE_1020_SLOPE_REG 0x72
+#define DS2780_AE_0010_SLOPE_REG 0x73
+#define DS2780_SE_3040_SLOPE_REG 0x74
+#define DS2780_SE_2030_SLOPE_REG 0x75
+#define DS2780_SE_1020_SLOPE_REG 0x76
+#define DS2780_SE_0010_SLOPE_REG 0x77
+#define DS2780_RSGAIN_MSB_REG 0x78
+#define DS2780_RSGAIN_LSB_REG 0x79
+#define DS2780_RSTC_REG 0x7A
+#define DS2780_FRSGAIN_MSB_REG 0x7B
+#define DS2780_FRSGAIN_LSB_REG 0x7C
+#define DS2780_EEPROM_BLOCK1_END 0x7C
+/* Register 0x7D - 0xFF Reserved */
+
+/* Number of valid register addresses */
+#define DS2780_DATA_SIZE 0x80
+
+/* Status register bits */
+#define DS2780_STATUS_REG_CHGTF (1 << 7)
+#define DS2780_STATUS_REG_AEF (1 << 6)
+#define DS2780_STATUS_REG_SEF (1 << 5)
+#define DS2780_STATUS_REG_LEARNF (1 << 4)
+/* Bit 3 Reserved */
+#define DS2780_STATUS_REG_UVF (1 << 2)
+#define DS2780_STATUS_REG_PORF (1 << 1)
+/* Bit 0 Reserved */
+
+/* Control register bits */
+/* Bit 7 Reserved */
+#define DS2780_CONTROL_REG_UVEN (1 << 6)
+#define DS2780_CONTROL_REG_PMOD (1 << 5)
+#define DS2780_CONTROL_REG_RNAOP (1 << 4)
+/* Bit 0 - 3 Reserved */
+
+/* Special feature register bits */
+/* Bit 1 - 7 Reserved */
+#define DS2780_SFR_REG_PIOSC (1 << 0)
+
+/* EEPROM register bits */
+#define DS2780_EEPROM_REG_EEC (1 << 7)
+#define DS2780_EEPROM_REG_LOCK (1 << 6)
+/* Bit 2 - 6 Reserved */
+#define DS2780_EEPROM_REG_BL1 (1 << 1)
+#define DS2780_EEPROM_REG_BL0 (1 << 0)
+
+extern int w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count,
+ int io);
+extern int w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd);
+
+#endif /* !_W1_DS2780_H */
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c
index b7b5014ff71..10606c82275 100644
--- a/drivers/w1/w1.c
+++ b/drivers/w1/w1.c
@@ -827,7 +827,7 @@ void w1_reconnect_slaves(struct w1_family *f, int attach)
mutex_unlock(&w1_mlock);
}
-static void w1_slave_found(struct w1_master *dev, u64 rn)
+void w1_slave_found(struct w1_master *dev, u64 rn)
{
struct w1_slave *sl;
struct w1_reg_num *tmp;
@@ -933,14 +933,15 @@ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb
}
}
-void w1_search_process(struct w1_master *dev, u8 search_type)
+void w1_search_process_cb(struct w1_master *dev, u8 search_type,
+ w1_slave_found_callback cb)
{
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);
+ w1_search_devices(dev, search_type, cb);
list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) {
if (!test_bit(W1_SLAVE_ACTIVE, (unsigned long *)&sl->flags) && !--sl->ttl)
@@ -953,6 +954,11 @@ void w1_search_process(struct w1_master *dev, u8 search_type)
dev->search_count--;
}
+static void w1_search_process(struct w1_master *dev, u8 search_type)
+{
+ w1_search_process_cb(dev, search_type, w1_slave_found);
+}
+
int w1_process(void *data)
{
struct w1_master *dev = (struct w1_master *) data;
diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h
index d8a9709f344..1ce23fc6186 100644
--- a/drivers/w1/w1.h
+++ b/drivers/w1/w1.h
@@ -55,6 +55,7 @@ struct w1_reg_num
#define W1_READ_ROM 0x33
#define W1_READ_PSUPPLY 0xB4
#define W1_MATCH_ROM 0x55
+#define W1_RESUME_CMD 0xA5
#define W1_SLAVE_ACTIVE 0
@@ -193,7 +194,9 @@ 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);
+void w1_slave_found(struct w1_master *dev, u64 rn);
+void w1_search_process_cb(struct w1_master *dev, u8 search_type,
+ w1_slave_found_callback cb);
struct w1_master *w1_search_master_id(u32 id);
/* Disconnect and reconnect devices in the given family. Used for finding
@@ -213,6 +216,7 @@ void w1_write_block(struct w1_master *, const u8 *, int);
void w1_touch_block(struct w1_master *, u8 *, int);
u8 w1_read_block(struct w1_master *, u8 *, int);
int w1_reset_select_slave(struct w1_slave *sl);
+int w1_reset_resume_command(struct w1_master *);
void w1_next_pullup(struct w1_master *, int);
static inline struct w1_slave* dev_to_w1_slave(struct device *dev)
diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h
index f3b636d7caf..97479ae70b9 100644
--- a/drivers/w1/w1_family.h
+++ b/drivers/w1/w1_family.h
@@ -34,8 +34,10 @@
#define W1_THERM_DS1822 0x22
#define W1_EEPROM_DS2433 0x23
#define W1_THERM_DS18B20 0x28
+#define W1_FAMILY_DS2408 0x29
#define W1_EEPROM_DS2431 0x2D
#define W1_FAMILY_DS2760 0x30
+#define W1_FAMILY_DS2780 0x32
#define MAXNAMELEN 32
diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c
index 3ebe9726a9e..8e8b64cfafb 100644
--- a/drivers/w1/w1_io.c
+++ b/drivers/w1/w1_io.c
@@ -390,6 +390,32 @@ int w1_reset_select_slave(struct w1_slave *sl)
EXPORT_SYMBOL_GPL(w1_reset_select_slave);
/**
+ * When the workflow with a slave amongst many requires several
+ * successive commands a reset between each, this function is similar
+ * to doing a reset then a match ROM for the last matched ROM. The
+ * advantage being that the matched ROM step is skipped in favor of the
+ * resume command. The slave must support the command of course.
+ *
+ * If the bus has only one slave, traditionnaly the match ROM is skipped
+ * and a "SKIP ROM" is done for efficiency. On multi-slave busses, this
+ * doesn't work of course, but the resume command is the next best thing.
+ *
+ * The w1 master lock must be held.
+ *
+ * @param dev the master device
+ */
+int w1_reset_resume_command(struct w1_master *dev)
+{
+ if (w1_reset_bus(dev))
+ return -1;
+
+ /* This will make only the last matched slave perform a skip ROM. */
+ w1_write_8(dev, W1_RESUME_CMD);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(w1_reset_resume_command);
+
+/**
* 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
diff --git a/drivers/w1/w1_netlink.c b/drivers/w1/w1_netlink.c
index 7e667bc77ef..55aabd927c6 100644
--- a/drivers/w1/w1_netlink.c
+++ b/drivers/w1/w1_netlink.c
@@ -55,6 +55,9 @@ static void w1_send_slave(struct w1_master *dev, u64 rn)
struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)(hdr + 1);
int avail;
+ /* update kernel slave list */
+ w1_slave_found(dev, rn);
+
avail = dev->priv_size - cmd->len;
if (avail > 8) {
@@ -85,7 +88,7 @@ static int w1_process_search_command(struct w1_master *dev, struct cn_msg *msg,
dev->priv = msg;
dev->priv_size = avail;
- w1_search_devices(dev, search_type, w1_send_slave);
+ w1_search_process_cb(dev, search_type, w1_send_slave);
msg->ack = 0;
cn_netlink_send(msg, 0, GFP_KERNEL);