diff options
Diffstat (limited to 'target/linux/brcm2708/patches-4.1/0119-Merge-pull-request-1059-from-pelwell-rpi-4.0.y.patch')
-rw-r--r-- | target/linux/brcm2708/patches-4.1/0119-Merge-pull-request-1059-from-pelwell-rpi-4.0.y.patch | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/target/linux/brcm2708/patches-4.1/0119-Merge-pull-request-1059-from-pelwell-rpi-4.0.y.patch b/target/linux/brcm2708/patches-4.1/0119-Merge-pull-request-1059-from-pelwell-rpi-4.0.y.patch new file mode 100644 index 0000000000..6280f75a48 --- /dev/null +++ b/target/linux/brcm2708/patches-4.1/0119-Merge-pull-request-1059-from-pelwell-rpi-4.0.y.patch @@ -0,0 +1,298 @@ +From 0dd4dae3071d135836946ab1b990061c0899e9b5 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <pelwell@users.noreply.github.com> +Date: Mon, 13 Jul 2015 13:25:31 +0100 +Subject: [PATCH 119/121] Merge pull request #1059 from pelwell/rpi-4.0.y + +w1_therm: Back-port locking improvements from 4.2-rc1 +--- + Documentation/ABI/stable/sysfs-driver-w1_ds28ea00 | 6 + + Documentation/w1/slaves/w1_therm | 11 +- + drivers/w1/slaves/w1_therm.c | 162 ++++++++++++++++++++-- + 3 files changed, 163 insertions(+), 16 deletions(-) + create mode 100644 Documentation/ABI/stable/sysfs-driver-w1_ds28ea00 + +--- /dev/null ++++ b/Documentation/ABI/stable/sysfs-driver-w1_ds28ea00 +@@ -0,0 +1,6 @@ ++What: /sys/bus/w1/devices/.../w1_seq ++Date: Apr 2015 ++Contact: Matt Campbell <mattrcampbell@gmail.com> ++Description: Support for the DS28EA00 chain sequence function ++ see Documentation/w1/slaves/w1_therm for detailed information ++Users: any user space application which wants to communicate with DS28EA00 +--- a/Documentation/w1/slaves/w1_therm ++++ b/Documentation/w1/slaves/w1_therm +@@ -11,12 +11,14 @@ Author: Evgeniy Polyakov <johnpol@2ka.mi + Description + ----------- + +-w1_therm provides basic temperature conversion for ds18*20 devices. ++w1_therm provides basic temperature conversion for ds18*20 devices, and the ++ds28ea00 device. + supported family codes: + W1_THERM_DS18S20 0x10 + W1_THERM_DS1822 0x22 + W1_THERM_DS18B20 0x28 + W1_THERM_DS1825 0x3B ++W1_THERM_DS28EA00 0x42 + + Support is provided through the sysfs w1_slave file. Each open and + read sequence will initiate a temperature conversion then provide two +@@ -48,3 +50,10 @@ resistor). The DS18b20 temperature sens + maximum current draw of 1.5mA and that a 5k pullup resistor is not + sufficient. The strong pullup is designed to provide the additional + current required. ++ ++The DS28EA00 provides an additional two pins for implementing a sequence ++detection algorithm. This feature allows you to determine the physical ++location of the chip in the 1-wire bus without needing pre-existing ++knowledge of the bus ordering. Support is provided through the sysfs ++w1_seq file. The file will contain a single line with an integer value ++representing the device index in the bus starting at 0. +--- a/drivers/w1/slaves/w1_therm.c ++++ b/drivers/w1/slaves/w1_therm.c +@@ -59,16 +59,32 @@ MODULE_ALIAS("w1-family-" __stringify(W1 + static int w1_strong_pullup = 1; + module_param_named(strong_pullup, w1_strong_pullup, int, 0); + ++struct w1_therm_family_data { ++ uint8_t rom[9]; ++ atomic_t refcnt; ++}; ++ ++/* return the address of the refcnt in the family data */ ++#define THERM_REFCNT(family_data) \ ++ (&((struct w1_therm_family_data*)family_data)->refcnt) ++ + static int w1_therm_add_slave(struct w1_slave *sl) + { +- sl->family_data = kzalloc(9, GFP_KERNEL); ++ sl->family_data = kzalloc(sizeof(struct w1_therm_family_data), ++ GFP_KERNEL); + if (!sl->family_data) + return -ENOMEM; ++ atomic_set(THERM_REFCNT(sl->family_data), 1); + return 0; + } + + static void w1_therm_remove_slave(struct w1_slave *sl) + { ++ int refcnt = atomic_sub_return(1, THERM_REFCNT(sl->family_data)); ++ while(refcnt) { ++ msleep(1000); ++ refcnt = atomic_read(THERM_REFCNT(sl->family_data)); ++ } + kfree(sl->family_data); + sl->family_data = NULL; + } +@@ -76,13 +92,24 @@ static void w1_therm_remove_slave(struct + static ssize_t w1_slave_show(struct device *device, + struct device_attribute *attr, char *buf); + ++static ssize_t w1_seq_show(struct device *device, ++ struct device_attribute *attr, char *buf); ++ + static DEVICE_ATTR_RO(w1_slave); ++static DEVICE_ATTR_RO(w1_seq); + + static struct attribute *w1_therm_attrs[] = { + &dev_attr_w1_slave.attr, + NULL, + }; ++ ++static struct attribute *w1_ds28ea00_attrs[] = { ++ &dev_attr_w1_slave.attr, ++ &dev_attr_w1_seq.attr, ++ NULL, ++}; + ATTRIBUTE_GROUPS(w1_therm); ++ATTRIBUTE_GROUPS(w1_ds28ea00); + + static struct w1_family_ops w1_therm_fops = { + .add_slave = w1_therm_add_slave, +@@ -90,6 +117,12 @@ static struct w1_family_ops w1_therm_fop + .groups = w1_therm_groups, + }; + ++static struct w1_family_ops w1_ds28ea00_fops = { ++ .add_slave = w1_therm_add_slave, ++ .remove_slave = w1_therm_remove_slave, ++ .groups = w1_ds28ea00_groups, ++}; ++ + static struct w1_family w1_therm_family_DS18S20 = { + .fid = W1_THERM_DS18S20, + .fops = &w1_therm_fops, +@@ -107,7 +140,7 @@ static struct w1_family w1_therm_family_ + + static struct w1_family w1_therm_family_DS28EA00 = { + .fid = W1_THERM_DS28EA00, +- .fops = &w1_therm_fops, ++ .fops = &w1_ds28ea00_fops, + }; + + static struct w1_family w1_therm_family_DS1825 = { +@@ -194,13 +227,22 @@ static ssize_t w1_slave_show(struct devi + struct w1_slave *sl = dev_to_w1_slave(device); + struct w1_master *dev = sl->master; + u8 rom[9], crc, verdict, external_power; +- int i, max_trying = 10; ++ int i, ret, max_trying = 10; + ssize_t c = PAGE_SIZE; ++ u8 *family_data = sl->family_data; + +- i = mutex_lock_interruptible(&dev->bus_mutex); +- if (i != 0) +- return i; ++ ret = mutex_lock_interruptible(&dev->bus_mutex); ++ if (ret != 0) ++ goto post_unlock; ++ ++ if(!sl->family_data) ++ { ++ ret = -ENODEV; ++ goto pre_unlock; ++ } + ++ /* prevent the slave from going away in sleep */ ++ atomic_inc(THERM_REFCNT(family_data)); + memset(rom, 0, sizeof(rom)); + + while (max_trying--) { +@@ -230,17 +272,19 @@ static ssize_t w1_slave_show(struct devi + mutex_unlock(&dev->bus_mutex); + + sleep_rem = msleep_interruptible(tm); +- if (sleep_rem != 0) +- return -EINTR; ++ if (sleep_rem != 0) { ++ ret = -EINTR; ++ goto post_unlock; ++ } + +- i = mutex_lock_interruptible(&dev->bus_mutex); +- if (i != 0) +- return i; ++ ret = mutex_lock_interruptible(&dev->bus_mutex); ++ if (ret != 0) ++ goto post_unlock; + } else if (!w1_strong_pullup) { + sleep_rem = msleep_interruptible(tm); + if (sleep_rem != 0) { +- mutex_unlock(&dev->bus_mutex); +- return -EINTR; ++ ret = -EINTR; ++ goto pre_unlock; + } + } + +@@ -269,19 +313,107 @@ static ssize_t w1_slave_show(struct devi + c -= snprintf(buf + PAGE_SIZE - c, c, ": crc=%02x %s\n", + crc, (verdict) ? "YES" : "NO"); + if (verdict) +- memcpy(sl->family_data, rom, sizeof(rom)); ++ memcpy(family_data, rom, sizeof(rom)); + else + dev_warn(device, "Read failed CRC check\n"); + + for (i = 0; i < 9; ++i) + c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", +- ((u8 *)sl->family_data)[i]); ++ ((u8 *)family_data)[i]); + + c -= snprintf(buf + PAGE_SIZE - c, c, "t=%d\n", + w1_convert_temp(rom, sl->family->fid)); ++ ret = PAGE_SIZE - c; ++ ++pre_unlock: + mutex_unlock(&dev->bus_mutex); + ++post_unlock: ++ atomic_dec(THERM_REFCNT(family_data)); ++ return ret; ++} ++ ++#define W1_42_CHAIN 0x99 ++#define W1_42_CHAIN_OFF 0x3C ++#define W1_42_CHAIN_OFF_INV 0xC3 ++#define W1_42_CHAIN_ON 0x5A ++#define W1_42_CHAIN_ON_INV 0xA5 ++#define W1_42_CHAIN_DONE 0x96 ++#define W1_42_CHAIN_DONE_INV 0x69 ++#define W1_42_COND_READ 0x0F ++#define W1_42_SUCCESS_CONFIRM_BYTE 0xAA ++#define W1_42_FINISHED_BYTE 0xFF ++static ssize_t w1_seq_show(struct device *device, ++ struct device_attribute *attr, char *buf) ++{ ++ struct w1_slave *sl = dev_to_w1_slave(device); ++ ssize_t c = PAGE_SIZE; ++ int rv; ++ int i; ++ u8 ack; ++ u64 rn; ++ struct w1_reg_num *reg_num; ++ int seq = 0; ++ ++ mutex_lock(&sl->master->bus_mutex); ++ /* Place all devices in CHAIN state */ ++ if (w1_reset_bus(sl->master)) ++ goto error; ++ w1_write_8(sl->master, W1_SKIP_ROM); ++ w1_write_8(sl->master, W1_42_CHAIN); ++ w1_write_8(sl->master, W1_42_CHAIN_ON); ++ w1_write_8(sl->master, W1_42_CHAIN_ON_INV); ++ msleep(sl->master->pullup_duration); ++ ++ /* check for acknowledgment */ ++ ack = w1_read_8(sl->master); ++ if (ack != W1_42_SUCCESS_CONFIRM_BYTE) ++ goto error; ++ ++ /* In case the bus fails to send 0xFF, limit*/ ++ for (i = 0; i <= 64; i++) { ++ if (w1_reset_bus(sl->master)) ++ goto error; ++ ++ w1_write_8(sl->master, W1_42_COND_READ); ++ rv = w1_read_block(sl->master, (u8 *)&rn, 8); ++ reg_num = (struct w1_reg_num *) &rn; ++ if (reg_num->family == W1_42_FINISHED_BYTE) ++ break; ++ if (sl->reg_num.id == reg_num->id) ++ seq = i; ++ ++ w1_write_8(sl->master, W1_42_CHAIN); ++ w1_write_8(sl->master, W1_42_CHAIN_DONE); ++ w1_write_8(sl->master, W1_42_CHAIN_DONE_INV); ++ w1_read_block(sl->master, &ack, sizeof(ack)); ++ ++ /* check for acknowledgment */ ++ ack = w1_read_8(sl->master); ++ if (ack != W1_42_SUCCESS_CONFIRM_BYTE) ++ goto error; ++ ++ } ++ ++ /* Exit from CHAIN state */ ++ if (w1_reset_bus(sl->master)) ++ goto error; ++ w1_write_8(sl->master, W1_SKIP_ROM); ++ w1_write_8(sl->master, W1_42_CHAIN); ++ w1_write_8(sl->master, W1_42_CHAIN_OFF); ++ w1_write_8(sl->master, W1_42_CHAIN_OFF_INV); ++ ++ /* check for acknowledgment */ ++ ack = w1_read_8(sl->master); ++ if (ack != W1_42_SUCCESS_CONFIRM_BYTE) ++ goto error; ++ mutex_unlock(&sl->master->bus_mutex); ++ ++ c -= snprintf(buf + PAGE_SIZE - c, c, "%d\n", seq); + return PAGE_SIZE - c; ++error: ++ mutex_unlock(&sl->master->bus_mutex); ++ return -EIO; + } + + static int __init w1_therm_init(void) |