aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm27xx/patches-5.4/950-0768-w1_therm-adding-ext_power-sysfs-entry.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/bcm27xx/patches-5.4/950-0768-w1_therm-adding-ext_power-sysfs-entry.patch')
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0768-w1_therm-adding-ext_power-sysfs-entry.patch293
1 files changed, 293 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-5.4/950-0768-w1_therm-adding-ext_power-sysfs-entry.patch b/target/linux/bcm27xx/patches-5.4/950-0768-w1_therm-adding-ext_power-sysfs-entry.patch
new file mode 100644
index 0000000000..b2e8195b64
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0768-w1_therm-adding-ext_power-sysfs-entry.patch
@@ -0,0 +1,293 @@
+From 5737e27d3c5e2f743421e68f926c05e8581ae21f Mon Sep 17 00:00:00 2001
+From: Akira Shimahara <akira215corp@gmail.com>
+Date: Mon, 11 May 2020 22:36:50 +0200
+Subject: [PATCH] w1_therm: adding ext_power sysfs entry
+
+commit b7bb6ca17a90f47c2fe2848531b5bbaf27a65ba7 upstream.
+
+Adding ext_power sysfs entry (RO). Return the power status of the device:
+ - 0: device parasite powered
+ - 1: device externally powered
+ - xx: xx is kernel error
+
+The power status of each device is check when the device is
+discover by the bus master, in 'w1_therm_add_slave(struct w1_slave *)'.
+The status is stored in the device structure w1_therm_family_data so
+that the driver always knows the power state of each device, which could
+be used later to determine the required strong pull up to apply on the
+line.
+
+The power status is re evaluate each time the sysfs ext_power read by
+a user.
+
+The hardware function 'read_powermode(struct w1_slave *sl)' act just as
+per device specifications, sending W1_READ_PSUPPLY command on the bus,
+and issue a read time slot, reading only one bit.
+
+A helper function 'bool bus_mutex_lock(struct mutex *lock)' is introduced.
+It try to aquire the bus mutex several times (W1_THERM_MAX_TRY), waiting
+W1_THERM_RETRY_DELAY between two attempt.
+
+Updating Documentation/ABI/testing/sysfs-driver-w1_therm accordingly.
+
+Signed-off-by: Akira Shimahara <akira215corp@gmail.com>
+Link: https://lore.kernel.org/r/20200511203650.410439-1-akira215corp@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ .../ABI/testing/sysfs-driver-w1_therm | 29 ++++
+ drivers/w1/slaves/w1_therm.c | 137 ++++++++++++++++++
+ 2 files changed, 166 insertions(+)
+ create mode 100644 Documentation/ABI/testing/sysfs-driver-w1_therm
+
+--- /dev/null
++++ b/Documentation/ABI/testing/sysfs-driver-w1_therm
+@@ -0,0 +1,29 @@
++What: /sys/bus/w1/devices/.../ext_power
++Date: May 2020
++Contact: Akira Shimahara <akira215corp@gmail.com>
++Description:
++ (RO) return the power status by asking the device
++ * '0': device parasite powered
++ * '1': device externally powered
++ * '-xx': xx is kernel error when reading power status
++Users: any user space application which wants to communicate with
++ w1_term device
++
++
++What: /sys/bus/w1/devices/.../w1_slave
++Date: May 2020
++Contact: Akira Shimahara <akira215corp@gmail.com>
++Description:
++ (RW) return the temperature in 1/1000 degC.
++ *read*: return 2 lines with the hexa output data sent on the
++ bus, return the CRC check and temperature in 1/1000 degC
++ *write* :
++ * '0' : save the 2 or 3 bytes to the device EEPROM
++ (i.e. TH, TL and config register)
++ * '9..12' : set the device resolution in RAM
++ (if supported)
++ * Anything else: do nothing
++ refer to Documentation/w1/slaves/w1_therm.rst for detailed
++ information.
++Users: any user space application which wants to communicate with
++ w1_term device
+\ No newline at end of file
+--- a/drivers/w1/slaves/w1_therm.c
++++ b/drivers/w1/slaves/w1_therm.c
+@@ -43,8 +43,21 @@
+ static int w1_strong_pullup = 1;
+ module_param_named(strong_pullup, w1_strong_pullup, int, 0);
+
++/* Nb of try for an operation */
++#define W1_THERM_MAX_TRY 5
++
++/* ms delay to retry bus mutex */
++#define W1_THERM_RETRY_DELAY 20
++
+ /* Helpers Macros */
+
++/*
++ * return the power mode of the sl slave : 1-ext, 0-parasite, <0 unknown
++ * always test family data existence before using this macro
++ */
++#define SLAVE_POWERMODE(sl) \
++ (((struct w1_therm_family_data *)(sl->family_data))->external_powered)
++
+ /* return the address of the refcnt in the family data */
+ #define THERM_REFCNT(family_data) \
+ (&((struct w1_therm_family_data *)family_data)->refcnt)
+@@ -73,10 +86,14 @@ struct w1_therm_family_converter {
+ * struct w1_therm_family_data - device data
+ * @rom: ROM device id (64bit Lasered ROM code + 1 CRC byte)
+ * @refcnt: ref count
++ * @external_powered: 1 device powered externally,
++ * 0 device parasite powered,
++ * -x error or undefined
+ */
+ struct w1_therm_family_data {
+ uint8_t rom[9];
+ atomic_t refcnt;
++ int external_powered;
+ };
+
+ /**
+@@ -109,6 +126,20 @@ struct therm_info {
+ */
+ static int reset_select_slave(struct w1_slave *sl);
+
++/**
++ * read_powermode() - Query the power mode of the slave
++ * @sl: slave to retrieve the power mode
++ *
++ * Ask the device to get its power mode (external or parasite)
++ * and store the power status in the &struct w1_therm_family_data.
++ *
++ * Return:
++ * * 0 parasite powered device
++ * * 1 externally powered device
++ * * <0 kernel error code
++ */
++static int read_powermode(struct w1_slave *sl);
++
+ /* Sysfs interface declaration */
+
+ static ssize_t w1_slave_show(struct device *device,
+@@ -120,10 +151,14 @@ static ssize_t w1_slave_store(struct dev
+ static ssize_t w1_seq_show(struct device *device,
+ struct device_attribute *attr, char *buf);
+
++static ssize_t ext_power_show(struct device *device,
++ struct device_attribute *attr, char *buf);
++
+ /* Attributes declarations */
+
+ static DEVICE_ATTR_RW(w1_slave);
+ static DEVICE_ATTR_RO(w1_seq);
++static DEVICE_ATTR_RO(ext_power);
+
+ /* Interface Functions declaration */
+
+@@ -151,12 +186,14 @@ static void w1_therm_remove_slave(struct
+
+ static struct attribute *w1_therm_attrs[] = {
+ &dev_attr_w1_slave.attr,
++ &dev_attr_ext_power.attr,
+ NULL,
+ };
+
+ static struct attribute *w1_ds28ea00_attrs[] = {
+ &dev_attr_w1_slave.attr,
+ &dev_attr_w1_seq.attr,
++ &dev_attr_ext_power.attr,
+ NULL,
+ };
+
+@@ -433,6 +470,34 @@ static struct w1_therm_family_converter
+ /* Helpers Functions */
+
+ /**
++ * bus_mutex_lock() - Acquire the mutex
++ * @lock: w1 bus mutex to acquire
++ *
++ * It try to acquire the mutex W1_THERM_MAX_TRY times and wait
++ * W1_THERM_RETRY_DELAY between 2 attempts.
++ *
++ * Return: true is mutex is acquired and lock, false otherwise
++ */
++static inline bool bus_mutex_lock(struct mutex *lock)
++{
++ int max_trying = W1_THERM_MAX_TRY;
++
++ /* try to acquire the mutex, if not, sleep retry_delay before retry) */
++ while (mutex_lock_interruptible(lock) != 0 && max_trying > 0) {
++ unsigned long sleep_rem;
++
++ sleep_rem = msleep_interruptible(W1_THERM_RETRY_DELAY);
++ if (!sleep_rem)
++ max_trying--;
++ }
++
++ if (!max_trying)
++ return false; /* Didn't acquire the bus mutex */
++
++ return true;
++}
++
++/**
+ * w1_convert_temp() - temperature conversion binding function
+ * @rom: data read from device RAM (8 data bytes + 1 CRC byte)
+ * @fid: device family id
+@@ -461,7 +526,19 @@ static int w1_therm_add_slave(struct w1_
+ GFP_KERNEL);
+ if (!sl->family_data)
+ return -ENOMEM;
++
+ atomic_set(THERM_REFCNT(sl->family_data), 1);
++
++ /* Getting the power mode of the device {external, parasite} */
++ SLAVE_POWERMODE(sl) = read_powermode(sl);
++
++ if (SLAVE_POWERMODE(sl) < 0) {
++ /* no error returned as device has been added */
++ dev_warn(&sl->dev,
++ "%s: Device has been added, but power_mode may be corrupted. err=%d\n",
++ __func__, SLAVE_POWERMODE(sl));
++ }
++
+ return 0;
+ }
+
+@@ -661,6 +738,44 @@ error:
+ return ret;
+ }
+
++static int read_powermode(struct w1_slave *sl)
++{
++ struct w1_master *dev_master = sl->master;
++ int max_trying = W1_THERM_MAX_TRY;
++ int ret = -ENODEV;
++
++ if (!sl->family_data)
++ goto error;
++
++ /* prevent the slave from going away in sleep */
++ atomic_inc(THERM_REFCNT(sl->family_data));
++
++ if (!bus_mutex_lock(&dev_master->bus_mutex)) {
++ ret = -EAGAIN; /* Didn't acquire the mutex */
++ goto dec_refcnt;
++ }
++
++ while ((max_trying--) && (ret < 0)) {
++ /* safe version to select slave */
++ if (!reset_select_slave(sl)) {
++ w1_write_8(dev_master, W1_READ_PSUPPLY);
++ /*
++ * Emit a read time slot and read only one bit,
++ * 1 is externally powered,
++ * 0 is parasite powered
++ */
++ ret = w1_touch_bit(dev_master, 1);
++ /* ret should be either 1 either 0 */
++ }
++ }
++ mutex_unlock(&dev_master->bus_mutex);
++
++dec_refcnt:
++ atomic_dec(THERM_REFCNT(sl->family_data));
++error:
++ return ret;
++}
++
+ /* Sysfs Interface definition */
+
+ static ssize_t w1_slave_show(struct device *device,
+@@ -722,6 +837,28 @@ static ssize_t w1_slave_store(struct dev
+ return ret ? : size;
+ }
+
++static ssize_t ext_power_show(struct device *device,
++ struct device_attribute *attr, char *buf)
++{
++ struct w1_slave *sl = dev_to_w1_slave(device);
++
++ if (!sl->family_data) {
++ dev_info(device,
++ "%s: Device not supported by the driver\n", __func__);
++ return 0; /* No device family */
++ }
++
++ /* Getting the power mode of the device {external, parasite} */
++ SLAVE_POWERMODE(sl) = read_powermode(sl);
++
++ if (SLAVE_POWERMODE(sl) < 0) {
++ dev_dbg(device,
++ "%s: Power_mode may be corrupted. err=%d\n",
++ __func__, SLAVE_POWERMODE(sl));
++ }
++ return sprintf(buf, "%d\n", SLAVE_POWERMODE(sl));
++}
++
+ #if IS_REACHABLE(CONFIG_HWMON)
+ static int w1_read_temp(struct device *device, u32 attr, int channel,
+ long *val)