aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm27xx/patches-5.4/950-0767-w1_therm-fix-reset_select_slave-during-discovery.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/bcm27xx/patches-5.4/950-0767-w1_therm-fix-reset_select_slave-during-discovery.patch')
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0767-w1_therm-fix-reset_select_slave-during-discovery.patch149
1 files changed, 149 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-5.4/950-0767-w1_therm-fix-reset_select_slave-during-discovery.patch b/target/linux/bcm27xx/patches-5.4/950-0767-w1_therm-fix-reset_select_slave-during-discovery.patch
new file mode 100644
index 0000000000..9881f81de4
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0767-w1_therm-fix-reset_select_slave-during-discovery.patch
@@ -0,0 +1,149 @@
+From dfc9fd0060f9103ca1f9335d529401ee8a105737 Mon Sep 17 00:00:00 2001
+From: Akira Shimahara <akira215corp@gmail.com>
+Date: Mon, 11 May 2020 22:36:10 +0200
+Subject: [PATCH] w1_therm: fix reset_select_slave during discovery
+
+commit c8ad65f6fbfdcb9b620674ef456020eef2bfeb36 upstream.
+
+Fix reset_select_slave issue during devices discovery by the master on
+bus. The w1_reset_select_slave() from w1_io.c, which was previously used,
+assume that if the slave count is 1 there is only one slave attached on
+the bus. This is not always true. For example when discovering devices,
+when the first device is discover by the bus master, its slave count is
+1, but some other slaves may be on the bus.
+
+In that case instead of adressing command to the attached slave the
+master throw a SKIP ROM command so that all slaves attached on the bus
+will answer simultenaously causing data collision.
+
+A dedicated reset_select_slave() function is implemented here,
+it always perform an adressing to each slave using the MATCH ROM
+command.
+
+Signed-off-by: Akira Shimahara <akira215corp@gmail.com>
+Link: https://lore.kernel.org/r/20200511203610.409975-1-akira215corp@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/w1/slaves/w1_therm.c | 48 ++++++++++++++++++++++++++++++------
+ 1 file changed, 41 insertions(+), 7 deletions(-)
+
+--- a/drivers/w1/slaves/w1_therm.c
++++ b/drivers/w1/slaves/w1_therm.c
+@@ -16,6 +16,7 @@
+ #include <linux/slab.h>
+ #include <linux/delay.h>
+ #include <linux/hwmon.h>
++#include <linux/string.h>
+
+ #include <linux/w1.h>
+
+@@ -90,6 +91,24 @@ struct therm_info {
+ u8 verdict;
+ };
+
++/* Hardware Functions declaration */
++
++/**
++ * reset_select_slave() - reset and select a slave
++ * @sl: the slave to select
++ *
++ * Resets the bus and select the slave by sending a ROM MATCH cmd
++ * w1_reset_select_slave() from w1_io.c could not be used here because
++ * it sent a SKIP ROM command if only one device is on the line.
++ * At the beginning of the such process, sl->master->slave_count is 1 even if
++ * more devices are on the line, causing collision on the line.
++ *
++ * Context: The w1 master lock must be held.
++ *
++ * Return: 0 if success, negative kernel error code otherwise.
++ */
++static int reset_select_slave(struct w1_slave *sl);
++
+ /* Sysfs interface declaration */
+
+ static ssize_t w1_slave_show(struct device *device,
+@@ -301,7 +320,7 @@ static inline int w1_DS18B20_precision(s
+ while (max_trying--) {
+ crc = 0;
+
+- if (!w1_reset_select_slave(sl)) {
++ if (!reset_select_slave(sl)) {
+ int count = 0;
+
+ /* read values to only alter precision bits */
+@@ -314,7 +333,7 @@ static inline int w1_DS18B20_precision(s
+ if (rom[8] == crc) {
+ rom[4] = (rom[4] & ~mask) | (precision_bits & mask);
+
+- if (!w1_reset_select_slave(sl)) {
++ if (!reset_select_slave(sl)) {
+ w1_write_8(dev, W1_WRITE_SCRATCHPAD);
+ w1_write_8(dev, rom[2]);
+ w1_write_8(dev, rom[3]);
+@@ -460,6 +479,21 @@ static void w1_therm_remove_slave(struct
+
+ /* Hardware Functions */
+
++/* Safe version of reset_select_slave - avoid using the one in w_io.c */
++static int reset_select_slave(struct w1_slave *sl)
++{
++ u8 match[9] = { W1_MATCH_ROM, };
++ u64 rn = le64_to_cpu(*((u64 *)&sl->reg_num));
++
++ if (w1_reset_bus(sl->master))
++ return -ENODEV;
++
++ memcpy(&match[1], &rn, 8);
++ w1_write_block(sl->master, match, 9);
++
++ return 0;
++}
++
+ static ssize_t read_therm(struct device *device,
+ struct w1_slave *sl, struct therm_info *info)
+ {
+@@ -487,7 +521,7 @@ static ssize_t read_therm(struct device
+ info->verdict = 0;
+ info->crc = 0;
+
+- if (!w1_reset_select_slave(sl)) {
++ if (!reset_select_slave(sl)) {
+ int count = 0;
+ unsigned int tm = 750;
+ unsigned long sleep_rem;
+@@ -495,7 +529,7 @@ static ssize_t read_therm(struct device
+ w1_write_8(dev, W1_READ_PSUPPLY);
+ external_power = w1_read_8(dev);
+
+- if (w1_reset_select_slave(sl))
++ if (reset_select_slave(sl))
+ continue;
+
+ /* 750ms strong pullup (or delay) after the convert */
+@@ -525,7 +559,7 @@ static ssize_t read_therm(struct device
+ }
+ }
+
+- if (!w1_reset_select_slave(sl)) {
++ if (!reset_select_slave(sl)) {
+
+ w1_write_8(dev, W1_READ_SCRATCHPAD);
+ count = w1_read_block(dev, info->rom, 9);
+@@ -577,7 +611,7 @@ static inline int w1_therm_eeprom(struct
+ memset(rom, 0, sizeof(rom));
+
+ while (max_trying--) {
+- if (!w1_reset_select_slave(sl)) {
++ if (!reset_select_slave(sl)) {
+ unsigned int tm = 10;
+ unsigned long sleep_rem;
+
+@@ -585,7 +619,7 @@ static inline int w1_therm_eeprom(struct
+ w1_write_8(dev, W1_READ_PSUPPLY);
+ external_power = w1_read_8(dev);
+
+- if (w1_reset_select_slave(sl))
++ if (reset_select_slave(sl))
+ continue;
+
+ /* 10ms strong pullup/delay after the copy command */