* [PATCH 0/5 v2] add 1-wire support @ 2012-10-29 12:46 Jean-Christophe PLAGNIOL-VILLARD 2012-10-29 13:02 ` [PATCH 1/5] clock: introduce non interruptible timeout Jean-Christophe PLAGNIOL-VILLARD 2012-10-29 22:18 ` [PATCH 0/5 v2] add 1-wire support Sascha Hauer 0 siblings, 2 replies; 8+ messages in thread From: Jean-Christophe PLAGNIOL-VILLARD @ 2012-10-29 12:46 UTC (permalink / raw) To: barebox Hi, v2: use is_non_interruptible_timeout as name use is_non_interruptible_timeout for ndelay as we will never call poller_cal anyway The following changes since commit 72703410feff9ed44779b54a41ff30312e781279: misc JTAG: include fixes (2012-10-26 09:08:51 +0200) are available in the git repository at: git://git.jcrosoft.org/barebox.git tags/w1 for you to fetch changes up to acaa716bce8cd1373cb5dabff54332fb1da7355b: 1-wire: add ds2433 support (2012-10-29 04:46:21 +0800) ---------------------------------------------------------------- add 1-wire support export for each device via param the familly id (fid) the id the full reg_num so for simple 64bit memory rom(ds2401/ds2411/ds1990*) no need driver. with ds2431 and ds2433 eeprom support Based on linux implementation, cleaned and re-implement the master/slave support to use the device/driver model correctly. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> ---------------------------------------------------------------- Jean-Christophe PLAGNIOL-VILLARD (5): clock: introduce non interruptible timeout add 1-wire support 1-wire: add gpio bus 1-wire: add ds2431 support 1-wire: add ds2433 support common/clock.c | 16 ++- drivers/Kconfig | 1 + drivers/Makefile | 1 + drivers/w1/Kconfig | 14 +++ drivers/w1/Makefile | 6 + drivers/w1/masters/Kconfig | 14 +++ drivers/w1/masters/Makefile | 5 + drivers/w1/masters/w1-gpio.c | 116 ++++++++++++++++++++ drivers/w1/slaves/Kconfig | 27 +++++ drivers/w1/slaves/Makefile | 6 + drivers/w1/slaves/w1_ds2431.c | 294 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/w1/slaves/w1_ds2433.c | 196 +++++++++++++++++++++++++++++++++ drivers/w1/w1.c | 621 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/w1/w1.h | 152 ++++++++++++++++++++++++++ include/clock.h | 1 + include/linux/w1-gpio.h | 25 +++++ 16 files changed, 1490 insertions(+), 5 deletions(-) create mode 100644 drivers/w1/Kconfig create mode 100644 drivers/w1/Makefile create mode 100644 drivers/w1/masters/Kconfig create mode 100644 drivers/w1/masters/Makefile create mode 100644 drivers/w1/masters/w1-gpio.c create mode 100644 drivers/w1/slaves/Kconfig create mode 100644 drivers/w1/slaves/Makefile create mode 100644 drivers/w1/slaves/w1_ds2431.c create mode 100644 drivers/w1/slaves/w1_ds2433.c create mode 100644 drivers/w1/w1.c create mode 100644 drivers/w1/w1.h create mode 100644 include/linux/w1-gpio.h Best Regards, J. _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/5] clock: introduce non interruptible timeout 2012-10-29 12:46 [PATCH 0/5 v2] add 1-wire support Jean-Christophe PLAGNIOL-VILLARD @ 2012-10-29 13:02 ` Jean-Christophe PLAGNIOL-VILLARD 2012-10-29 13:02 ` [PATCH 2/5] add 1-wire support Jean-Christophe PLAGNIOL-VILLARD ` (3 more replies) 2012-10-29 22:18 ` [PATCH 0/5 v2] add 1-wire support Sascha Hauer 1 sibling, 4 replies; 8+ messages in thread From: Jean-Christophe PLAGNIOL-VILLARD @ 2012-10-29 13:02 UTC (permalink / raw) To: barebox is_timeout call poller_call if the timeout is >= 100us but on 1-wire bus we need to wait 500us and not more than 930us for the bus reset. So if the poller_call is caller we can not guarantee it. So for this introduce is_non_interruptible_timeout than we only wait. Use it for ndelay too. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> --- common/clock.c | 16 +++++++++++----- include/clock.h | 1 + 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/common/clock.c b/common/clock.c index 8adbeaf..9c7c1ba 100644 --- a/common/clock.c +++ b/common/clock.c @@ -135,23 +135,29 @@ uint32_t clocksource_hz2mult(uint32_t hz, uint32_t shift_constant) return (uint32_t)tmp; } -int is_timeout(uint64_t start_ns, uint64_t time_offset_ns) +int is_timeout_non_interruptible(uint64_t start_ns, uint64_t time_offset_ns) { - if (time_offset_ns >= 100 * USECOND) - poller_call(); - if ((int64_t)(start_ns + time_offset_ns - get_time_ns()) < 0) return 1; else return 0; } +EXPORT_SYMBOL(is_timeout_non_interruptible); + +int is_timeout(uint64_t start_ns, uint64_t time_offset_ns) +{ + if (time_offset_ns >= 100 * USECOND) + poller_call(); + + return is_timeout_non_interruptible(start_ns, time_offset_ns); +} EXPORT_SYMBOL(is_timeout); void ndelay(unsigned long nsecs) { uint64_t start = get_time_ns(); - while(!is_timeout(start, nsecs)); + while(!is_timeout_non_interruptible(start, nsecs)); } EXPORT_SYMBOL(ndelay); diff --git a/include/clock.h b/include/clock.h index c01a8d0..a169790 100644 --- a/include/clock.h +++ b/include/clock.h @@ -32,6 +32,7 @@ void clocks_calc_mult_shift(uint32_t *mult, uint32_t *shift, uint32_t from, uint uint32_t clocksource_hz2mult(uint32_t hz, uint32_t shift_constant); int is_timeout(uint64_t start_ns, uint64_t time_offset_ns); +int is_timeout_non_interruptible(uint64_t start_ns, uint64_t time_offset_ns); // void udelay(unsigned long usecs); -- 1.7.10.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 2/5] add 1-wire support 2012-10-29 13:02 ` [PATCH 1/5] clock: introduce non interruptible timeout Jean-Christophe PLAGNIOL-VILLARD @ 2012-10-29 13:02 ` Jean-Christophe PLAGNIOL-VILLARD 2012-10-29 13:02 ` [PATCH 3/5] 1-wire: add gpio bus Jean-Christophe PLAGNIOL-VILLARD ` (2 subsequent siblings) 3 siblings, 0 replies; 8+ messages in thread From: Jean-Christophe PLAGNIOL-VILLARD @ 2012-10-29 13:02 UTC (permalink / raw) To: barebox export for each device via param the familly id (fid) the id the full reg_num so for simple 64bit memory rom(ds2401/ds2411/ds1990*) no need driver. Based on linux implementation, cleaned and re-implement the master/slave support to use the device/driver model correctly. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> --- drivers/Kconfig | 1 + drivers/Makefile | 1 + drivers/w1/Kconfig | 14 + drivers/w1/Makefile | 6 + drivers/w1/masters/Kconfig | 7 + drivers/w1/masters/Makefile | 4 + drivers/w1/slaves/Kconfig | 7 + drivers/w1/slaves/Makefile | 4 + drivers/w1/w1.c | 621 +++++++++++++++++++++++++++++++++++++++++++ drivers/w1/w1.h | 152 +++++++++++ 10 files changed, 817 insertions(+) create mode 100644 drivers/w1/Kconfig create mode 100644 drivers/w1/Makefile create mode 100644 drivers/w1/masters/Kconfig create mode 100644 drivers/w1/masters/Makefile create mode 100644 drivers/w1/slaves/Kconfig create mode 100644 drivers/w1/slaves/Makefile create mode 100644 drivers/w1/w1.c create mode 100644 drivers/w1/w1.h diff --git a/drivers/Kconfig b/drivers/Kconfig index d0b5e3a..945fe02 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -21,5 +21,6 @@ source "drivers/pwm/Kconfig" source "drivers/dma/Kconfig" source "drivers/gpio/Kconfig" source "drivers/of/Kconfig" +source "drivers/w1/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 2a1f8b0..e6cdf39 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -21,3 +21,4 @@ obj-y += dma/ obj-y += watchdog/ obj-y += gpio/ obj-$(CONFIG_OFDEVICE) += of/ +obj-$(CONFIG_W1) += w1/ diff --git a/drivers/w1/Kconfig b/drivers/w1/Kconfig new file mode 100644 index 0000000..ab34997 --- /dev/null +++ b/drivers/w1/Kconfig @@ -0,0 +1,14 @@ +menuconfig W1 + bool "Dallas's 1-wire support" + ---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. + +if W1 + +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..3e36d2f --- /dev/null +++ b/drivers/w1/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the Dallas's 1-wire bus. +# + +obj-$(CONFIG_W1) += w1.o +obj-y += masters/ slaves/ diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig new file mode 100644 index 0000000..86aa8df --- /dev/null +++ b/drivers/w1/masters/Kconfig @@ -0,0 +1,7 @@ +# +# 1-wire bus master configuration +# + +menu "1-wire Bus Masters" + +endmenu diff --git a/drivers/w1/masters/Makefile b/drivers/w1/masters/Makefile new file mode 100644 index 0000000..ed1b9d9 --- /dev/null +++ b/drivers/w1/masters/Makefile @@ -0,0 +1,4 @@ +# +# Makefile for 1-wire bus master drivers. +# + diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig new file mode 100644 index 0000000..f35eedf --- /dev/null +++ b/drivers/w1/slaves/Kconfig @@ -0,0 +1,7 @@ +# +# 1-wire slaves configuration +# + +menu "1-wire Slaves" + +endmenu diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile new file mode 100644 index 0000000..ac9a5a7 --- /dev/null +++ b/drivers/w1/slaves/Makefile @@ -0,0 +1,4 @@ +# +# Makefile for the Dallas's 1-wire slaves. +# + diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c new file mode 100644 index 0000000..11b8320 --- /dev/null +++ b/drivers/w1/w1.c @@ -0,0 +1,621 @@ +/* + * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net> + * Copyright (c) 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> + * + * 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. + */ + +#include <common.h> +#include <init.h> +#include <clock.h> + +#include "w1.h" + +static LIST_HEAD(w1_buses); + +static void w1_pre_write(struct w1_bus *bus); +static void w1_post_write(struct w1_bus *bus); + +static void w1_delay(unsigned long usecs) +{ + uint64_t start = get_time_ns(); + + while(!is_timeout_non_interruptible(start, usecs * USECOND)); +} + +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 +}; + +u8 w1_calc_crc8(u8 * data, int len) +{ + u8 crc = 0; + + while (len--) + crc = w1_crc8_table[crc ^ *data++]; + + return crc; +} + +/** + * 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_bus *bus) +{ + return bus->reset_bus(bus) & 0x1; +} + +static u8 w1_soft_reset_bus(struct w1_bus *bus) +{ + int result; + + bus->write_bit(bus, 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(500); + bus->write_bit(bus, 1); + w1_delay(70); + + result = bus->read_bit(bus) & 0x1; + /* minmum 70 (above) + 430 = 500 us + * There aren't any timing requirements between a reset and + * the following transactions. Sleeping is safe here. + */ + /* w1_delay(430); min required time */ + mdelay(1); + + return result; +} + +/** + * Generates a write-0 or write-1 cycle and samples the level. + */ +static u8 w1_touch_bit(struct w1_bus *bus, int bit) +{ + return bus->touch_bit(bus, bit); +} + +/** + * 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_bus *bus, int bit) +{ + if (bit) { + bus->write_bit(bus, 0); + w1_delay(6); + bus->write_bit(bus, 1); + w1_delay(64); + } else { + bus->write_bit(bus, 0); + w1_delay(60); + bus->write_bit(bus, 1); + w1_delay(10); + } +} + +/** + * Reads 8 bits. + * + * @param dev the master device + * @return the byte read + */ +u8 w1_read_8(struct w1_bus *bus) +{ + return bus->read_byte(bus); +} + +static u8 w1_soft_read_8(struct w1_bus *bus) +{ + int i; + u8 res = 0; + + for (i = 0; i < 8; ++i) + res |= (w1_touch_bit(bus, 1) << i); + + return res; +} + +/** + * 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 + */ +void w1_write_block(struct w1_bus *bus, const u8 *buf, int len) +{ + int i; + + if (bus->write_block) { + w1_pre_write(bus); + bus->write_block(bus, buf, len); + } else { + for (i = 0; i < len; ++i) + w1_write_8(bus, buf[i]); /* calls w1_pre_write */ + } + w1_post_write(bus); +} + +/** + * Touches 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 + */ +void w1_touch_block(struct w1_bus *bus, u8 *buf, int len) +{ + int i, j; + u8 tmp; + + for (i = 0; i < len; ++i) { + tmp = 0; + for (j = 0; j < 8; ++j) { + if (j == 7) + w1_pre_write(bus); + tmp |= w1_touch_bit(bus, (buf[i] >> j) & 0x1) << j; + } + + buf[i] = tmp; + } +} + +/** + * 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_bus *bus) +{ + int result; + + /* sample timing is critical here */ + bus->write_bit(bus, 0); + w1_delay(6); + bus->write_bit(bus, 1); + w1_delay(9); + + result = bus->read_bit(bus); + + 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_bus *bus, int bdir) +{ + return bus->triplet(bus, bdir); +} + +static u8 w1_soft_triplet(struct w1_bus *bus, u8 bdir) +{ + u8 id_bit = w1_touch_bit(bus, 1); + u8 comp_bit = w1_touch_bit(bus, 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; + } + + w1_touch_bit(bus, bdir); + return retval; +} + +static u8 w1_soft_touch_bit(struct w1_bus *bus, u8 bit) +{ + if (bit) + return w1_read_bit(bus); + + w1_write_bit(bus, 0); + return 0; +} + +/** + * 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_bus *bus) +{ + if (!bus->set_pullup) + return; + + if (bus->pullup_duration && bus->enable_pullup) + bus->set_pullup(bus, bus->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_bus *bus) +{ + if (!bus->pullup_duration) + return; + + if (bus->enable_pullup && bus->set_pullup) + bus->set_pullup(bus, 0); + else + mdelay(bus->pullup_duration); + bus->pullup_duration = 0; +} + +/** + * Writes 8 bits. + * + * @param dev the master device + * @param byte the byte to write + */ +void w1_write_8(struct w1_bus *bus, u8 byte) +{ + if (bus->write_byte) { + w1_pre_write(bus); + bus->write_byte(bus, byte); + } else { + int i; + + for (i = 0; i < 8; ++i) { + if (i == 7) + w1_pre_write(bus); + w1_touch_bit(bus, (byte >> i) & 0x1); + } + } + w1_post_write(bus); +} + +/** + * 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_bus *bus, u8 *buf, int len) +{ + return bus->read_block(bus, buf, len); +} + +static u8 w1_soft_read_block(struct w1_bus *bus, u8 *buf, int len) +{ + int i; + + for (i = 0; i < len; ++i) + buf[i] = w1_read_8(bus); + return len; +} + +/** + * 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_device *dev) +{ + struct w1_bus *bus = dev->bus; + + if (w1_reset_bus(bus)) + return -1; + + if (bus->slave_count == 1) + w1_write_8(bus, W1_SKIP_ROM); + else { + u8 match[9] = {W1_MATCH_ROM, }; + u64 rn = le64_to_cpu(*((u64*)&dev->reg_num)); + + memcpy(&match[1], &rn, 8); + w1_write_block(bus, match, 9); + } + return 0; +} + +#define to_w1_device(d) container_of(d, struct w1_device, dev) +#define to_w1_driver(d) container_of(d, struct w1_driver, drv) + +static int w1_bus_match(struct device_d *_dev, struct driver_d *_drv) +{ + struct w1_device *dev = to_w1_device(_dev); + struct w1_driver *drv = to_w1_driver(_drv); + + return !(drv->fid == dev->fid); +} + +static int w1_bus_probe(struct device_d *_dev) +{ + struct w1_driver *drv = to_w1_driver(_dev->driver); + struct w1_device *dev = to_w1_device(_dev); + + return drv->probe(dev); +} + +static void w1_bus_remove(struct device_d *_dev) +{ + struct w1_driver *drv = to_w1_driver(_dev->driver); + struct w1_device *dev = to_w1_device(_dev); + + return drv->remove(dev); +} + +struct bus_type w1_bustype= { + .name = "w1_bus", + .match = w1_bus_match, + .probe = w1_bus_probe, + .remove = w1_bus_remove, +}; + +static int w1_device_register(struct w1_bus *bus, struct w1_device *dev) +{ + char str[18]; + int ret; + + sprintf(dev->dev.name, "w1-%x-", dev->fid); + dev->dev.id = DEVICE_ID_DYNAMIC; + dev->dev.bus = &w1_bustype; + dev->bus = bus; + + dev->dev.parent = &bus->dev; + dev_add_child(dev->dev.parent, &dev->dev); + + ret = register_device(&dev->dev); + if (ret) + return ret; + + sprintf(str, "0x%x", dev->fid); + dev_add_param_fixed(&dev->dev, "fid", str); + sprintf(str, "0x%llx", dev->id); + dev_add_param_fixed(&dev->dev, "id", str); + sprintf(str, "0x%llx", dev->reg_num); + dev_add_param_fixed(&dev->dev, "reg_num", str); + + return ret; +} + +int w1_driver_register(struct w1_driver *drv) +{ + drv->drv.bus = &w1_bustype; + + if (drv->probe) + drv->drv.probe = w1_bus_probe; + if (drv->remove) + drv->drv.remove = w1_bus_remove; + + return register_driver(&drv->drv); +} + +void w1_found(struct w1_bus *bus, u64 rn) +{ + struct w1_device *dev = xzalloc(sizeof(*dev)); + u64 tmp = be64_to_cpu(rn); + + dev->reg_num = rn; + dev->fid = tmp >> 56; + dev->id = (tmp >> 8) & 0xffffffffffff; + dev->crc = tmp & 0xff; + + dev_dbg(&bus->dev, "%s: familly = 0x%x, id = 0x%llx, crc = 0x%x\n", + __func__, dev->fid, dev->id, dev->crc); + + if (dev->crc != w1_calc_crc8((u8 *)&rn, 7)) { + dev_err(&bus->dev, "0x%llx crc error\n", rn); + return; + } + + w1_device_register(bus, dev); +} + +/** + * 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 + */ +static void w1_search(struct w1_bus *bus, u8 search_type) +{ + 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++ < bus->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(bus)) { + dev_dbg(&bus->dev, "No devices present on the wire.\n"); + break; + } + + /* Do fast search on single slave bus */ + if (bus->max_slave_count == 1) { + int rv; + w1_write_8(bus, W1_READ_ROM); + rv = w1_read_block(bus, (u8 *)&rn, 8); + + if (rv == 8 && rn) + w1_found(bus, rn); + + break; + } + + /* Start the search */ + w1_write_8(bus, 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(bus, 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 ( (triplet_ret & 0x03) != 0x03 ) { + if ( (desc_bit == last_zero) || (last_zero < 0)) + last_device = 1; + desc_bit = last_zero; + w1_found(bus, rn); + } + } + + w1_reset_bus(bus); +} + +int w1_bus_register(struct w1_bus *bus) +{ + int ret; + + /* validate minimum functionality */ + if (!(bus->touch_bit && bus->reset_bus) && + !(bus->write_bit && bus->read_bit)) { + pr_err("w1_bus_register: 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 hardware 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 (!bus->touch_bit && bus->set_pullup) { + pr_err("w1_add_bus_device: set_pullup requires touch_bit, disabling\n"); + bus->set_pullup = NULL; + } + + if (!bus->reset_bus) + bus->reset_bus = w1_soft_reset_bus; + + if (!bus->touch_bit) + bus->touch_bit = w1_soft_touch_bit; + + if (!bus->read_block) + bus->read_block = w1_soft_read_block; + + if (!bus->read_byte) + bus->read_byte = w1_soft_read_8; + + if (!bus->triplet) + bus->triplet = w1_soft_triplet; + + if (!bus->max_slave_count) + bus->max_slave_count = 10; + + list_add_tail(&bus->list, &w1_buses); + + strcpy(bus->dev.name, "w1_bus"); + bus->dev.id = DEVICE_ID_DYNAMIC; + + bus->dev.parent = bus->parent; + if (bus->parent) + dev_add_child(bus->parent, &bus->dev); + + ret = register_device(&bus->dev); + if (ret) + return ret; + + w1_search(bus, W1_SEARCH); + + return 0; +} + +static int w1_bus_init(void) +{ + return bus_register(&w1_bustype); +} +pure_initcall(w1_bus_init); diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h new file mode 100644 index 0000000..1373f69 --- /dev/null +++ b/drivers/w1/w1.h @@ -0,0 +1,152 @@ +/* + * w1.h + * + * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net> + * Copyright (c) 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> + * + * 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. + */ + +#ifndef __W1_H +#define __W1_H + +#include <common.h> +#include <driver.h> + +struct w1_device { + u64 reg_num; + u8 fid; + u64 id; + u8 crc; + + struct w1_bus *bus; + struct device_d dev; +}; + +struct w1_driver { + u8 fid; + u64 id; + + int (*probe) (struct w1_device *dev); + void (*remove) (struct w1_device *dev); + struct driver_d drv; +}; + +int w1_driver_register(struct w1_driver *drv); + +extern struct bus_type w1_bustype; + +#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_RESUME_CMD 0xA5 + +#define W1_SLAVE_ACTIVE 0 + +/** + * 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 +{ + struct device_d dev; + struct device_d *parent; + + /** + * Sample the line level + * @return the level read (0 or 1) + */ + u8 (*read_bit)(struct w1_bus *); + + /** Sets the line level */ + void (*write_bit)(struct w1_bus *, 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)(struct w1_bus *, u8); + + /** + * Reads a bytes. Same as 8 touch_bit(1) calls. + * @return the byte read + */ + u8 (*read_byte)(struct w1_bus *); + + /** + * Writes a byte. Same as 8 touch_bit(x) calls. + */ + void (*write_byte)(struct w1_bus *, u8); + + /** + * Same as a series of read_byte() calls + * @return the number of bytes read + */ + u8 (*read_block)(struct w1_bus *, u8 *, int); + + /** Same as a series of write_byte() calls */ + void (*write_block)(struct w1_bus *, const u8 *, int); + + /** + * Combines two reads and a smart write for ROM searches + * @return bit0=Id bit1=comp_id bit2=dir_taken + */ + u8 (*triplet)(struct w1_bus *, 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)(struct w1_bus *); + + /** + * Put out a strong pull-up pulse of the specified duration. + * @return -1=Error, 0=completed + */ + u8 (*set_pullup)(struct w1_bus *, int); + + /** 5V strong pullup enabled flag, 1 enabled, zero disabled. */ + int enable_pullup; + /** 5V strong pullup duration in milliseconds, zero disabled. */ + int pullup_duration; + + int max_slave_count, slave_count; + + void *data; + struct list_head list; +}; + +u8 w1_read_block(struct w1_bus *bus, u8 *buf, int len); +void w1_write_block(struct w1_bus *bus, const u8 *buf, int len); +void w1_touch_block(struct w1_bus *bus, u8 *buf, int len); +u8 w1_triplet(struct w1_bus *bus, int bdir); +u8 w1_read_8(struct w1_bus *bus); +void w1_write_8(struct w1_bus *bus, u8 byte); +int w1_reset_select_slave(struct w1_device *dev); +int w1_reset_bus(struct w1_bus *bus); +u8 w1_calc_crc8(u8 * data, int len); + +int w1_bus_register(struct w1_bus *bus); + +#endif /* __W1_H */ -- 1.7.10.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 3/5] 1-wire: add gpio bus 2012-10-29 13:02 ` [PATCH 1/5] clock: introduce non interruptible timeout Jean-Christophe PLAGNIOL-VILLARD 2012-10-29 13:02 ` [PATCH 2/5] add 1-wire support Jean-Christophe PLAGNIOL-VILLARD @ 2012-10-29 13:02 ` Jean-Christophe PLAGNIOL-VILLARD 2012-10-29 13:02 ` [PATCH 4/5] 1-wire: add ds2431 support Jean-Christophe PLAGNIOL-VILLARD 2012-10-29 13:02 ` [PATCH 5/5] 1-wire: add ds2433 support Jean-Christophe PLAGNIOL-VILLARD 3 siblings, 0 replies; 8+ messages in thread From: Jean-Christophe PLAGNIOL-VILLARD @ 2012-10-29 13:02 UTC (permalink / raw) To: barebox Based on linux implementation. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> --- drivers/w1/masters/Kconfig | 7 +++ drivers/w1/masters/Makefile | 1 + drivers/w1/masters/w1-gpio.c | 116 ++++++++++++++++++++++++++++++++++++++++++ include/linux/w1-gpio.h | 25 +++++++++ 4 files changed, 149 insertions(+) create mode 100644 drivers/w1/masters/w1-gpio.c create mode 100644 include/linux/w1-gpio.h diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig index 86aa8df..e429c14 100644 --- a/drivers/w1/masters/Kconfig +++ b/drivers/w1/masters/Kconfig @@ -4,4 +4,11 @@ menu "1-wire Bus Masters" +config W1_MASTER_GPIO + bool "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. + endmenu diff --git a/drivers/w1/masters/Makefile b/drivers/w1/masters/Makefile index ed1b9d9..84b35bb 100644 --- a/drivers/w1/masters/Makefile +++ b/drivers/w1/masters/Makefile @@ -2,3 +2,4 @@ # Makefile for 1-wire bus master drivers. # +obj-$(CONFIG_W1_MASTER_GPIO) += w1-gpio.o diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c new file mode 100644 index 0000000..0a3794d --- /dev/null +++ b/drivers/w1/masters/w1-gpio.c @@ -0,0 +1,116 @@ +/* + * w1-gpio - GPIO w1 bus master driver + * + * Copyright (C) 2007 Ville Syrjala <syrjala@sci.fi> + * Copyright (c) 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> + * + * 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 <common.h> +#include <init.h> +#include <malloc.h> +#include <xfuncs.h> +#include <driver.h> +#include <linux/w1-gpio.h> +#include <gpio.h> + +#include "../w1.h" + +static void w1_gpio_write_bit_dir(struct w1_bus *bus, u8 bit) +{ + struct w1_gpio_platform_data *pdata = bus->data; + + if (bit) + gpio_direction_input(pdata->pin); + else + gpio_direction_output(pdata->pin, 0); +} + +static void w1_gpio_write_bit_val(struct w1_bus *bus, u8 bit) +{ + struct w1_gpio_platform_data *pdata = bus->data; + + gpio_set_value(pdata->pin, bit); +} + +static u8 w1_gpio_read_bit(struct w1_bus *bus) +{ + struct w1_gpio_platform_data *pdata = bus->data; + + return gpio_get_value(pdata->pin) ? 1 : 0; +} + +static int __init w1_gpio_probe(struct device_d *dev) +{ + struct w1_bus *master; + struct w1_gpio_platform_data *pdata; + int err; + + pdata = dev->platform_data; + + if (!pdata) + return -ENXIO; + + master = xzalloc(sizeof(struct w1_bus)); + + err = gpio_request(pdata->pin, "w1"); + if (err) + goto free_master; + + if (gpio_is_valid(pdata->ext_pullup_enable_pin)) { + err = gpio_request(pdata->pin, "w1 pullup"); + if (err < 0) + goto free_gpio; + + gpio_direction_output(pdata->pin, 0); + } + + 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; + } + + master->parent = dev; + + err = w1_bus_register(master); + if (err) + goto free_gpio_ext_pu; + + if (pdata->enable_external_pullup) + pdata->enable_external_pullup(1); + + if (gpio_is_valid(pdata->ext_pullup_enable_pin)) + gpio_set_value(pdata->ext_pullup_enable_pin, 1); + + return 0; + + free_gpio_ext_pu: + if (gpio_is_valid(pdata->ext_pullup_enable_pin)) + gpio_free(pdata->ext_pullup_enable_pin); + free_gpio: + gpio_free(pdata->pin); + free_master: + kfree(master); + + return err; +} + +static struct driver_d w1_gpio_driver = { + .name = "w1-gpio", + .probe = w1_gpio_probe, +}; + +static int __init w1_gpio_init(void) +{ + return platform_driver_register(&w1_gpio_driver); +} +device_initcall(w1_gpio_init); diff --git a/include/linux/w1-gpio.h b/include/linux/w1-gpio.h new file mode 100644 index 0000000..065e3ae --- /dev/null +++ b/include/linux/w1-gpio.h @@ -0,0 +1,25 @@ +/* + * w1-gpio interface to platform code + * + * 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. + */ +#ifndef _LINUX_W1_GPIO_H +#define _LINUX_W1_GPIO_H + +/** + * struct w1_gpio_platform_data - Platform-dependent data for w1-gpio + * @pin: GPIO pin to use + * @is_open_drain: GPIO pin is configured as open drain + */ +struct w1_gpio_platform_data { + unsigned int pin; + unsigned int is_open_drain:1; + void (*enable_external_pullup)(int enable); + unsigned int ext_pullup_enable_pin; +}; + +#endif /* _LINUX_W1_GPIO_H */ -- 1.7.10.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 4/5] 1-wire: add ds2431 support 2012-10-29 13:02 ` [PATCH 1/5] clock: introduce non interruptible timeout Jean-Christophe PLAGNIOL-VILLARD 2012-10-29 13:02 ` [PATCH 2/5] add 1-wire support Jean-Christophe PLAGNIOL-VILLARD 2012-10-29 13:02 ` [PATCH 3/5] 1-wire: add gpio bus Jean-Christophe PLAGNIOL-VILLARD @ 2012-10-29 13:02 ` Jean-Christophe PLAGNIOL-VILLARD 2012-10-29 13:02 ` [PATCH 5/5] 1-wire: add ds2433 support Jean-Christophe PLAGNIOL-VILLARD 3 siblings, 0 replies; 8+ messages in thread From: Jean-Christophe PLAGNIOL-VILLARD @ 2012-10-29 13:02 UTC (permalink / raw) To: barebox Based on linux implementation. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> --- drivers/w1/slaves/Kconfig | 10 ++ drivers/w1/slaves/Makefile | 1 + drivers/w1/slaves/w1_ds2431.c | 294 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 305 insertions(+) create mode 100644 drivers/w1/slaves/w1_ds2431.c diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index f35eedf..26150a2 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -4,4 +4,14 @@ menu "1-wire Slaves" +config W1_SLAVE_DS2431 + bool "1kb EEPROM family support (DS2431)" + help + Say Y here if you want to use a 1-wire + 1kb EEPROM family device (DS2431) + +config W1_SLAVE_DS2431_WRITE + bool "write support" + depends on W1_SLAVE_DS2431 + endmenu diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index ac9a5a7..65804e0 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile @@ -2,3 +2,4 @@ # Makefile for the Dallas's 1-wire slaves. # +obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o diff --git a/drivers/w1/slaves/w1_ds2431.c b/drivers/w1/slaves/w1_ds2431.c new file mode 100644 index 0000000..30e6e1d --- /dev/null +++ b/drivers/w1/slaves/w1_ds2431.c @@ -0,0 +1,294 @@ +/* + * w1_ds2431.c - w1 family 2d (DS2431) driver + * + * Copyright (c) 2008 Bernhard Weirich <bernhard.weirich@riedel.net> + * Copyright (c) 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> + * + * 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 <init.h> +#include "../w1.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 + +#define DRIVERNAME "ds2431" + +static int ds2431_count = 0; + +/* + * 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 ds2431_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. + */ +int ds2431_readblock(struct w1_device *dev, int off, int count, char *buf) +{ + struct w1_bus *bus = dev->bus; + 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(dev)) + return -1; + + w1_write_block(bus, wrbuf, 3); + w1_read_block(bus, buf, count); + + if (w1_reset_select_slave(dev)) + return -1; + + w1_write_block(bus, wrbuf, 3); + w1_read_block(bus, cmp, count); + + if (!memcmp(cmp, buf, count)) + return 0; + } while (--tries); + + dev_err(&dev->dev, "proof reading failed %d times\n", + W1_F2D_READ_RETRIES); + + return -1; +} + +static ssize_t ds2431_cdev_read(struct cdev *cdev, void *buf, size_t count, + loff_t off, ulong flags) +{ + struct w1_device *dev = cdev->priv; + int todo = count; + + count = ds2431_fix_count(off, count, W1_F2D_EEPROM_SIZE); + if (count == 0) + return 0; + + /* 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 (ds2431_readblock(dev, off, block_read, buf) < 0) + count = -EIO; + + todo -= W1_F2D_READ_MAXLEN; + buf += W1_F2D_READ_MAXLEN; + off += W1_F2D_READ_MAXLEN; + } + + return count; +} + +#ifdef CONFIG_W1_SLAVE_DS2431_WRITE +/* + * 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 ds2431_write(struct w1_device *dev, int addr, int len, const u8 *data) +{ + struct w1_bus *bus = dev->bus; + 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(dev)) + return -1; + + wrbuf[0] = W1_F2D_WRITE_SCRATCH; + wrbuf[1] = addr & 0xff; + wrbuf[2] = addr >> 8; + + w1_write_block(bus, wrbuf, 3); + w1_write_block(bus, data, len); + + /* Read the scratchpad and verify */ + if (w1_reset_select_slave(dev)) + return -1; + + w1_write_8(bus, W1_F2D_READ_SCRATCH); + w1_read_block(bus, 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(&dev->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(dev)) + return -1; + + wrbuf[0] = W1_F2D_COPY_SCRATCH; + wrbuf[3] = es; + w1_write_block(bus, wrbuf, 4); + + /* Sleep for tprog ms to wait for the write to complete */ + mdelay(W1_F2D_TPROG_MS); + + /* Reset the bus to wake up the EEPROM */ + w1_reset_bus(bus); + + return 0; +} + +static ssize_t ds2431_cdev_write(struct cdev *cdev, const void *buf, size_t count, + loff_t off, ulong flags) +{ + struct w1_device *dev = cdev->priv; + int addr, len; + int copy; + + count = ds2431_fix_count(off, count, W1_F2D_EEPROM_SIZE); + if (count == 0) + return 0; + + /* 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 (ds2431_readblock(dev, 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 (ds2431_write(dev, addr & ~W1_F2D_SCRATCH_MASK, + W1_F2D_SCRATCH_SIZE, tmp) < 0) { + count = -EIO; + goto out_up; + } + } else { + + copy = W1_F2D_SCRATCH_SIZE; + if (ds2431_write(dev, addr, copy, buf) < 0) { + count = -EIO; + goto out_up; + } + } + buf += copy; + addr += copy; + len -= copy; + } + +out_up: + return count; +} +#else +#define ds2431_cdev_write NULL +#endif + +static struct file_operations ds2431_ops = { + .read = ds2431_cdev_read, + .write = ds2431_cdev_write, + .lseek = dev_lseek_default, +}; + +static int ds2431_probe(struct w1_device *dev) +{ + struct cdev *cdev; + + cdev = xzalloc(sizeof(*cdev)); + cdev->dev = &dev->dev; + cdev->priv = dev; + cdev->ops = &ds2431_ops; + cdev->size = W1_F2D_EEPROM_SIZE; + cdev->name = asprintf(DRIVERNAME"%d", ds2431_count++); + if (cdev->name == NULL) + return -ENOMEM; + + return devfs_create(cdev); +} + +struct w1_driver ds2431_driver = { + .drv = { + .name = DRIVERNAME, + }, + .probe = ds2431_probe, + .fid = 0x2d, +}; + +static int w1_ds2431_init(void) +{ + return w1_driver_register(&ds2431_driver); +} +device_initcall(w1_ds2431_init); -- 1.7.10.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 5/5] 1-wire: add ds2433 support 2012-10-29 13:02 ` [PATCH 1/5] clock: introduce non interruptible timeout Jean-Christophe PLAGNIOL-VILLARD ` (2 preceding siblings ...) 2012-10-29 13:02 ` [PATCH 4/5] 1-wire: add ds2431 support Jean-Christophe PLAGNIOL-VILLARD @ 2012-10-29 13:02 ` Jean-Christophe PLAGNIOL-VILLARD 3 siblings, 0 replies; 8+ messages in thread From: Jean-Christophe PLAGNIOL-VILLARD @ 2012-10-29 13:02 UTC (permalink / raw) To: barebox Based on linux implementation. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> --- drivers/w1/slaves/Kconfig | 10 +++ drivers/w1/slaves/Makefile | 1 + drivers/w1/slaves/w1_ds2433.c | 196 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 207 insertions(+) create mode 100644 drivers/w1/slaves/w1_ds2433.c diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index 26150a2..946a0b3 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -14,4 +14,14 @@ config W1_SLAVE_DS2431_WRITE bool "write support" depends on W1_SLAVE_DS2431 +config W1_SLAVE_DS2433 + bool "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_WRITE + bool "write support" + depends on W1_SLAVE_DS2433 + endmenu diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index 65804e0..dd7160a 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o +obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o diff --git a/drivers/w1/slaves/w1_ds2433.c b/drivers/w1/slaves/w1_ds2433.c new file mode 100644 index 0000000..51279fb --- /dev/null +++ b/drivers/w1/slaves/w1_ds2433.c @@ -0,0 +1,196 @@ +/* + * 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 <init.h> +#include "../w1.h" + +#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 + +#define DRIVERNAME "ds2433" + +static int ds2433_count = 0; + +/** + * 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 ds2433_fix_count(loff_t off, size_t count, size_t size) +{ + if (off > size) + return 0; + + if ((off + count) > size) + return (size - off); + + return count; +} + +static ssize_t ds2433_cdev_read(struct cdev *cdev, void *buf, size_t count, + loff_t off, ulong flags) +{ + struct w1_device *dev = cdev->priv; + struct w1_bus *bus = dev->bus; + u8 wrbuf[3]; + + if ((count = ds2433_fix_count(off, count, W1_EEPROM_SIZE)) == 0) + return 0; + + /* read directly from the EEPROM */ + if (w1_reset_select_slave(dev)) { + count = -EIO; + goto out_up; + } + + wrbuf[0] = W1_F23_READ_EEPROM; + wrbuf[1] = off & 0xff; + wrbuf[2] = off >> 8; + w1_write_block(bus, wrbuf, 3); + w1_read_block(bus, buf, count); + +out_up: + return count; +} + +#ifdef CONFIG_W1_SLAVE_DS2433_WRITE +/** + * 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 ds2433_write(struct w1_device *dev, int addr, int len, const u8 *data) +{ + struct w1_bus *bus = dev->bus; + 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(dev)) + return -1; + + wrbuf[0] = W1_F23_WRITE_SCRATCH; + wrbuf[1] = addr & 0xff; + wrbuf[2] = addr >> 8; + + w1_write_block(bus, wrbuf, 3); + w1_write_block(bus, data, len); + + /* Read the scratchpad and verify */ + if (w1_reset_select_slave(dev)) + return -1; + + w1_write_8(bus, W1_F23_READ_SCRATCH); + w1_read_block(bus, 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(dev)) + return -1; + + wrbuf[0] = W1_F23_COPY_SCRATCH; + wrbuf[3] = es; + w1_write_block(bus, wrbuf, 4); + + /* Sleep for 5 ms to wait for the write to complete */ + mdelay(5); + + /* Reset the bus to wake up the EEPROM (this may not be needed) */ + w1_reset_bus(bus); + return 0; +} + +static ssize_t ds2433_cdev_write(struct cdev *cdev, const void *buf, size_t count, + loff_t off, ulong flags) +{ + struct w1_device *dev = cdev->priv; + int addr, len, idx; + const u8 *buf8 = buf; + + if ((count = ds2433_fix_count(off, count, W1_EEPROM_SIZE)) == 0) + return 0; + + /* 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 (ds2433_write(dev, addr, len, &buf8[idx]) < 0) { + count = -EIO; + goto out_up; + } + idx += len; + } + +out_up: + return count; +} +#else +#define ds2433_cdev_write NULL +#endif + +static struct file_operations ds2433_ops = { + .read = ds2433_cdev_read, + .write = ds2433_cdev_write, + .lseek = dev_lseek_default, +}; + +static int ds2433_probe(struct w1_device *dev) +{ + struct cdev *cdev; + + cdev = xzalloc(sizeof(*cdev)); + cdev->dev = &dev->dev; + cdev->priv = dev; + cdev->ops = &ds2433_ops; + cdev->size = W1_EEPROM_SIZE; + cdev->name = asprintf(DRIVERNAME"%d", ds2433_count++); + if (cdev->name == NULL) + return -ENOMEM; + + return devfs_create(cdev); +} + +struct w1_driver ds2433_driver = { + .drv = { + .name = DRIVERNAME, + }, + .probe = ds2433_probe, + .fid = 0x23, +}; + +static int w1_ds2433_init(void) +{ + return w1_driver_register(&ds2433_driver); +} +device_initcall(w1_ds2433_init); -- 1.7.10.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 0/5 v2] add 1-wire support 2012-10-29 12:46 [PATCH 0/5 v2] add 1-wire support Jean-Christophe PLAGNIOL-VILLARD 2012-10-29 13:02 ` [PATCH 1/5] clock: introduce non interruptible timeout Jean-Christophe PLAGNIOL-VILLARD @ 2012-10-29 22:18 ` Sascha Hauer 1 sibling, 0 replies; 8+ messages in thread From: Sascha Hauer @ 2012-10-29 22:18 UTC (permalink / raw) To: Jean-Christophe PLAGNIOL-VILLARD; +Cc: barebox On Mon, Oct 29, 2012 at 01:46:09PM +0100, Jean-Christophe PLAGNIOL-VILLARD wrote: > Hi, > > v2: use is_non_interruptible_timeout as name > use is_non_interruptible_timeout for ndelay as we will never call poller_cal anyway > > The following changes since commit 72703410feff9ed44779b54a41ff30312e781279: > > misc JTAG: include fixes (2012-10-26 09:08:51 +0200) > > are available in the git repository at: > > git://git.jcrosoft.org/barebox.git tags/w1 > Applied, thanks Sascha > for you to fetch changes up to acaa716bce8cd1373cb5dabff54332fb1da7355b: > > 1-wire: add ds2433 support (2012-10-29 04:46:21 +0800) > > ---------------------------------------------------------------- > add 1-wire support > > export for each device via param > the familly id (fid) > the id > the full reg_num > > so for simple 64bit memory rom(ds2401/ds2411/ds1990*) no need driver. > > with ds2431 and ds2433 eeprom support > > Based on linux implementation, cleaned and re-implement the master/slave > support to use the device/driver model correctly. > > Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> > > ---------------------------------------------------------------- > Jean-Christophe PLAGNIOL-VILLARD (5): > clock: introduce non interruptible timeout > add 1-wire support > 1-wire: add gpio bus > 1-wire: add ds2431 support > 1-wire: add ds2433 support > > common/clock.c | 16 ++- > drivers/Kconfig | 1 + > drivers/Makefile | 1 + > drivers/w1/Kconfig | 14 +++ > drivers/w1/Makefile | 6 + > drivers/w1/masters/Kconfig | 14 +++ > drivers/w1/masters/Makefile | 5 + > drivers/w1/masters/w1-gpio.c | 116 ++++++++++++++++++++ > drivers/w1/slaves/Kconfig | 27 +++++ > drivers/w1/slaves/Makefile | 6 + > drivers/w1/slaves/w1_ds2431.c | 294 +++++++++++++++++++++++++++++++++++++++++++++++++ > drivers/w1/slaves/w1_ds2433.c | 196 +++++++++++++++++++++++++++++++++ > drivers/w1/w1.c | 621 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > drivers/w1/w1.h | 152 ++++++++++++++++++++++++++ > include/clock.h | 1 + > include/linux/w1-gpio.h | 25 +++++ > 16 files changed, 1490 insertions(+), 5 deletions(-) > create mode 100644 drivers/w1/Kconfig > create mode 100644 drivers/w1/Makefile > create mode 100644 drivers/w1/masters/Kconfig > create mode 100644 drivers/w1/masters/Makefile > create mode 100644 drivers/w1/masters/w1-gpio.c > create mode 100644 drivers/w1/slaves/Kconfig > create mode 100644 drivers/w1/slaves/Makefile > create mode 100644 drivers/w1/slaves/w1_ds2431.c > create mode 100644 drivers/w1/slaves/w1_ds2433.c > create mode 100644 drivers/w1/w1.c > create mode 100644 drivers/w1/w1.h > create mode 100644 include/linux/w1-gpio.h > > Best Regards, > J. > > _______________________________________________ > barebox mailing list > barebox@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/barebox > -- Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 0/5] add 1-wire support @ 2012-10-27 19:59 Jean-Christophe PLAGNIOL-VILLARD 2012-10-27 20:03 ` [PATCH 1/5] clock: introduce non interruptible timeout Jean-Christophe PLAGNIOL-VILLARD 0 siblings, 1 reply; 8+ messages in thread From: Jean-Christophe PLAGNIOL-VILLARD @ 2012-10-27 19:59 UTC (permalink / raw) To: barebox Hi, The following changes since commit 72703410feff9ed44779b54a41ff30312e781279: misc JTAG: include fixes (2012-10-26 09:08:51 +0200) are available in the git repository at: git://git.jcrosoft.org/barebox.git tags/w1 for you to fetch changes up to 91413767b35d5f9f66d14376c925386a42d1db0e: 1-wire: add ds2433 support (2012-10-27 12:47:20 +0800) ---------------------------------------------------------------- add 1-wire support export for each device via param the familly id (fid) the id the full reg_num so for simple 64bit memory rom(ds2401/ds2411/ds1990*) no need driver. with ds2431 and ds2433 eeprom support Based on linux implementation, cleaned and re-implement the master/slave support to use the device/driver model correctly. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> ---------------------------------------------------------------- Jean-Christophe PLAGNIOL-VILLARD (5): clock: introduce non interruptible timeout add 1-wire support 1-wire: add gpio bus 1-wire: add ds2431 support 1-wire: add ds2433 support common/clock.c | 14 ++- drivers/Kconfig | 1 + drivers/Makefile | 1 + drivers/w1/Kconfig | 14 +++ drivers/w1/Makefile | 6 + drivers/w1/masters/Kconfig | 14 +++ drivers/w1/masters/Makefile | 5 + drivers/w1/masters/w1-gpio.c | 116 ++++++++++++++++++++ drivers/w1/slaves/Kconfig | 27 +++++ drivers/w1/slaves/Makefile | 6 + drivers/w1/slaves/w1_ds2431.c | 294 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/w1/slaves/w1_ds2433.c | 196 +++++++++++++++++++++++++++++++++ drivers/w1/w1.c | 621 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/w1/w1.h | 152 ++++++++++++++++++++++++++ include/clock.h | 1 + include/linux/w1-gpio.h | 25 +++++ 16 files changed, 1489 insertions(+), 4 deletions(-) create mode 100644 drivers/w1/Kconfig create mode 100644 drivers/w1/Makefile create mode 100644 drivers/w1/masters/Kconfig create mode 100644 drivers/w1/masters/Makefile create mode 100644 drivers/w1/masters/w1-gpio.c create mode 100644 drivers/w1/slaves/Kconfig create mode 100644 drivers/w1/slaves/Makefile create mode 100644 drivers/w1/slaves/w1_ds2431.c create mode 100644 drivers/w1/slaves/w1_ds2433.c create mode 100644 drivers/w1/w1.c create mode 100644 drivers/w1/w1.h create mode 100644 include/linux/w1-gpio.h Best Regards, J. _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/5] clock: introduce non interruptible timeout 2012-10-27 19:59 [PATCH 0/5] " Jean-Christophe PLAGNIOL-VILLARD @ 2012-10-27 20:03 ` Jean-Christophe PLAGNIOL-VILLARD 2012-10-27 20:03 ` [PATCH 4/5] 1-wire: add ds2431 support Jean-Christophe PLAGNIOL-VILLARD 0 siblings, 1 reply; 8+ messages in thread From: Jean-Christophe PLAGNIOL-VILLARD @ 2012-10-27 20:03 UTC (permalink / raw) To: barebox is_non_timeout call poller_call if the timeout is >= 100us but on 1-wire bus we need to wait 500us and not more than 930us for the bus reset. So if the poller_call is caller we can not guarantee it. So for this introduce is_non_interruptible_timeout than we only wait. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> --- common/clock.c | 14 ++++++++++---- include/clock.h | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/common/clock.c b/common/clock.c index 8adbeaf..a358b78 100644 --- a/common/clock.c +++ b/common/clock.c @@ -135,16 +135,22 @@ uint32_t clocksource_hz2mult(uint32_t hz, uint32_t shift_constant) return (uint32_t)tmp; } -int is_timeout(uint64_t start_ns, uint64_t time_offset_ns) +int is_non_interruptible_timeout(uint64_t start_ns, uint64_t time_offset_ns) { - if (time_offset_ns >= 100 * USECOND) - poller_call(); - if ((int64_t)(start_ns + time_offset_ns - get_time_ns()) < 0) return 1; else return 0; } +EXPORT_SYMBOL(is_non_interruptable_timeout); + +int is_timeout(uint64_t start_ns, uint64_t time_offset_ns) +{ + if (time_offset_ns >= 100 * USECOND) + poller_call(); + + return is_non_interruptible_timeout(start_ns, time_offset_ns); +} EXPORT_SYMBOL(is_timeout); void ndelay(unsigned long nsecs) diff --git a/include/clock.h b/include/clock.h index c01a8d0..5fd9134 100644 --- a/include/clock.h +++ b/include/clock.h @@ -32,6 +32,7 @@ void clocks_calc_mult_shift(uint32_t *mult, uint32_t *shift, uint32_t from, uint uint32_t clocksource_hz2mult(uint32_t hz, uint32_t shift_constant); int is_timeout(uint64_t start_ns, uint64_t time_offset_ns); +int is_non_interruptible_timeout(uint64_t start_ns, uint64_t time_offset_ns); // void udelay(unsigned long usecs); -- 1.7.10.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 4/5] 1-wire: add ds2431 support 2012-10-27 20:03 ` [PATCH 1/5] clock: introduce non interruptible timeout Jean-Christophe PLAGNIOL-VILLARD @ 2012-10-27 20:03 ` Jean-Christophe PLAGNIOL-VILLARD 0 siblings, 0 replies; 8+ messages in thread From: Jean-Christophe PLAGNIOL-VILLARD @ 2012-10-27 20:03 UTC (permalink / raw) To: barebox Based on linux implementation. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> --- drivers/w1/slaves/Kconfig | 10 ++ drivers/w1/slaves/Makefile | 1 + drivers/w1/slaves/w1_ds2431.c | 294 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 305 insertions(+) create mode 100644 drivers/w1/slaves/w1_ds2431.c diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index f35eedf..26150a2 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -4,4 +4,14 @@ menu "1-wire Slaves" +config W1_SLAVE_DS2431 + bool "1kb EEPROM family support (DS2431)" + help + Say Y here if you want to use a 1-wire + 1kb EEPROM family device (DS2431) + +config W1_SLAVE_DS2431_WRITE + bool "write support" + depends on W1_SLAVE_DS2431 + endmenu diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index ac9a5a7..65804e0 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile @@ -2,3 +2,4 @@ # Makefile for the Dallas's 1-wire slaves. # +obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o diff --git a/drivers/w1/slaves/w1_ds2431.c b/drivers/w1/slaves/w1_ds2431.c new file mode 100644 index 0000000..30e6e1d --- /dev/null +++ b/drivers/w1/slaves/w1_ds2431.c @@ -0,0 +1,294 @@ +/* + * w1_ds2431.c - w1 family 2d (DS2431) driver + * + * Copyright (c) 2008 Bernhard Weirich <bernhard.weirich@riedel.net> + * Copyright (c) 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> + * + * 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 <init.h> +#include "../w1.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 + +#define DRIVERNAME "ds2431" + +static int ds2431_count = 0; + +/* + * 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 ds2431_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. + */ +int ds2431_readblock(struct w1_device *dev, int off, int count, char *buf) +{ + struct w1_bus *bus = dev->bus; + 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(dev)) + return -1; + + w1_write_block(bus, wrbuf, 3); + w1_read_block(bus, buf, count); + + if (w1_reset_select_slave(dev)) + return -1; + + w1_write_block(bus, wrbuf, 3); + w1_read_block(bus, cmp, count); + + if (!memcmp(cmp, buf, count)) + return 0; + } while (--tries); + + dev_err(&dev->dev, "proof reading failed %d times\n", + W1_F2D_READ_RETRIES); + + return -1; +} + +static ssize_t ds2431_cdev_read(struct cdev *cdev, void *buf, size_t count, + loff_t off, ulong flags) +{ + struct w1_device *dev = cdev->priv; + int todo = count; + + count = ds2431_fix_count(off, count, W1_F2D_EEPROM_SIZE); + if (count == 0) + return 0; + + /* 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 (ds2431_readblock(dev, off, block_read, buf) < 0) + count = -EIO; + + todo -= W1_F2D_READ_MAXLEN; + buf += W1_F2D_READ_MAXLEN; + off += W1_F2D_READ_MAXLEN; + } + + return count; +} + +#ifdef CONFIG_W1_SLAVE_DS2431_WRITE +/* + * 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 ds2431_write(struct w1_device *dev, int addr, int len, const u8 *data) +{ + struct w1_bus *bus = dev->bus; + 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(dev)) + return -1; + + wrbuf[0] = W1_F2D_WRITE_SCRATCH; + wrbuf[1] = addr & 0xff; + wrbuf[2] = addr >> 8; + + w1_write_block(bus, wrbuf, 3); + w1_write_block(bus, data, len); + + /* Read the scratchpad and verify */ + if (w1_reset_select_slave(dev)) + return -1; + + w1_write_8(bus, W1_F2D_READ_SCRATCH); + w1_read_block(bus, 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(&dev->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(dev)) + return -1; + + wrbuf[0] = W1_F2D_COPY_SCRATCH; + wrbuf[3] = es; + w1_write_block(bus, wrbuf, 4); + + /* Sleep for tprog ms to wait for the write to complete */ + mdelay(W1_F2D_TPROG_MS); + + /* Reset the bus to wake up the EEPROM */ + w1_reset_bus(bus); + + return 0; +} + +static ssize_t ds2431_cdev_write(struct cdev *cdev, const void *buf, size_t count, + loff_t off, ulong flags) +{ + struct w1_device *dev = cdev->priv; + int addr, len; + int copy; + + count = ds2431_fix_count(off, count, W1_F2D_EEPROM_SIZE); + if (count == 0) + return 0; + + /* 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 (ds2431_readblock(dev, 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 (ds2431_write(dev, addr & ~W1_F2D_SCRATCH_MASK, + W1_F2D_SCRATCH_SIZE, tmp) < 0) { + count = -EIO; + goto out_up; + } + } else { + + copy = W1_F2D_SCRATCH_SIZE; + if (ds2431_write(dev, addr, copy, buf) < 0) { + count = -EIO; + goto out_up; + } + } + buf += copy; + addr += copy; + len -= copy; + } + +out_up: + return count; +} +#else +#define ds2431_cdev_write NULL +#endif + +static struct file_operations ds2431_ops = { + .read = ds2431_cdev_read, + .write = ds2431_cdev_write, + .lseek = dev_lseek_default, +}; + +static int ds2431_probe(struct w1_device *dev) +{ + struct cdev *cdev; + + cdev = xzalloc(sizeof(*cdev)); + cdev->dev = &dev->dev; + cdev->priv = dev; + cdev->ops = &ds2431_ops; + cdev->size = W1_F2D_EEPROM_SIZE; + cdev->name = asprintf(DRIVERNAME"%d", ds2431_count++); + if (cdev->name == NULL) + return -ENOMEM; + + return devfs_create(cdev); +} + +struct w1_driver ds2431_driver = { + .drv = { + .name = DRIVERNAME, + }, + .probe = ds2431_probe, + .fid = 0x2d, +}; + +static int w1_ds2431_init(void) +{ + return w1_driver_register(&ds2431_driver); +} +device_initcall(w1_ds2431_init); -- 1.7.10.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2012-10-29 22:18 UTC | newest] Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2012-10-29 12:46 [PATCH 0/5 v2] add 1-wire support Jean-Christophe PLAGNIOL-VILLARD 2012-10-29 13:02 ` [PATCH 1/5] clock: introduce non interruptible timeout Jean-Christophe PLAGNIOL-VILLARD 2012-10-29 13:02 ` [PATCH 2/5] add 1-wire support Jean-Christophe PLAGNIOL-VILLARD 2012-10-29 13:02 ` [PATCH 3/5] 1-wire: add gpio bus Jean-Christophe PLAGNIOL-VILLARD 2012-10-29 13:02 ` [PATCH 4/5] 1-wire: add ds2431 support Jean-Christophe PLAGNIOL-VILLARD 2012-10-29 13:02 ` [PATCH 5/5] 1-wire: add ds2433 support Jean-Christophe PLAGNIOL-VILLARD 2012-10-29 22:18 ` [PATCH 0/5 v2] add 1-wire support Sascha Hauer -- strict thread matches above, loose matches on Subject: below -- 2012-10-27 19:59 [PATCH 0/5] " Jean-Christophe PLAGNIOL-VILLARD 2012-10-27 20:03 ` [PATCH 1/5] clock: introduce non interruptible timeout Jean-Christophe PLAGNIOL-VILLARD 2012-10-27 20:03 ` [PATCH 4/5] 1-wire: add ds2431 support Jean-Christophe PLAGNIOL-VILLARD
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox