f07e572f64
bcm2708: boot tested on RPi B+ v1.2 bcm2709: boot tested on RPi 3B v1.2 and RPi 4B v1.1 4G bcm2710: boot tested on RPi 3B v1.2 bcm2711: boot tested on RPi 4B v1.1 4G Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
294 lines
8.6 KiB
Diff
294 lines
8.6 KiB
Diff
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)
|