mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-23 15:32:33 +00:00
150 lines
4.7 KiB
Diff
150 lines
4.7 KiB
Diff
|
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 */
|