Patch for enhacement of w1_therm module. 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
Creating Documentation/ABI/testing/sysfs-driver-w1_therm for the old driver sysfs and this new entry. Signed-off-by: Akira Shimahara <akira215c...@gmail.com> --- .../ABI/testing/sysfs-driver-w1_therm | 29 ++++++ drivers/w1/slaves/w1_therm.c | 93 ++++++++++++++++++- drivers/w1/slaves/w1_therm.h | 44 ++++++++- 3 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-driver-w1_therm diff --git a/Documentation/ABI/testing/sysfs-driver-w1_therm b/Documentation/ABI/testing/sysfs-driver-w1_therm new file mode 100644 index 0000000..9aaf625 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-w1_therm @@ -0,0 +1,29 @@ +What: /sys/bus/w1/devices/.../ext_power +Date: Apr 2020 +Contact: Akira Shimahara <akira215c...@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: Apr 2020 +Contact: Akira Shimahara <akira215c...@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 diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index 6245950..a530853 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -39,12 +39,14 @@ module_param_named(strong_pullup, w1_strong_pullup, int, 0); 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, }; @@ -294,6 +296,26 @@ static inline int w1_DS18S20_convert_temp(u8 rom[9]) return t; } +/*------------------------ Helpers Functions----------------------------*/ + +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; +} + /*-------------------------Interface Functions------------------------------*/ static int w1_therm_add_slave(struct w1_slave *sl) { @@ -302,6 +324,16 @@ static int w1_therm_add_slave(struct w1_slave *sl) 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; } @@ -512,6 +544,43 @@ 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); + /* 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; +} + /*------------------------Interface sysfs--------------------------*/ static ssize_t w1_slave_show(struct device *device, @@ -565,13 +634,35 @@ static ssize_t w1_slave_store(struct device *device, ret = w1_therm_families[i].eeprom(device); else ret = w1_therm_families[i].precision(device, - val); + val); break; } } 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) diff --git a/drivers/w1/slaves/w1_therm.h b/drivers/w1/slaves/w1_therm.h index b73af0b..2f975a4 100644 --- a/drivers/w1/slaves/w1_therm.h +++ b/drivers/w1/slaves/w1_therm.h @@ -25,6 +25,12 @@ #include <linux/mutex.h> #include <linux/w1.h> +/*----------------------------------Defines---------------------------------*/ +/* Nb of try for an operation */ +#define W1_THERM_MAX_TRY 5 + +/* ms delay to retry bus mutex */ +#define W1_THERM_RETRY_DELAY 20 /*----------------------------------Structs---------------------------------*/ /** @@ -47,10 +53,15 @@ struct w1_therm_family_converter { * struct w1_therm_family_data * @param rom data * @param refcnt ref count + * @param 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; }; /** @@ -80,11 +91,24 @@ static inline int w1_DS18B20_convert_temp(u8 rom[9]); static inline int w1_DS18S20_convert_temp(u8 rom[9]); /*-------------------------------Macros--------------------------------------*/ +/* return the power mode of the sl slave : 1-ext, 0-parasite, <0 unknown + * always test family data existence before + */ +#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) +/*-------------------------- Helpers Functions------------------------------*/ + +/** bus_mutex_lock() get the mutex & retry + * @param lock w1 bus mutex to get + * @return value true is mutex is acquired and lock, false otherwise + */ +static inline bool bus_mutex_lock(struct mutex *lock); + /*---------------------------Hardware Functions-----------------------------*/ /** @@ -107,7 +131,14 @@ static int reset_select_slave(struct w1_slave *sl); */ static ssize_t read_therm(struct device *device, struct w1_slave *sl, struct therm_info *info); - +/** read_powermode() + * @brief ask the device to get its power mode {external, parasite} + * @param sl slave to be interrogated + * @return 0 parasite powered device + * 1 externally powered device + * <0 kernel error code + */ +static int read_powermode(struct w1_slave *sl); /*----------------------------Interface sysfs-------------------------------*/ /** @brief A callback function to output the temperature Old way @@ -127,11 +158,20 @@ static ssize_t w1_slave_store(struct device *device, static ssize_t w1_seq_show(struct device *device, struct device_attribute *attr, char *buf); - +/** @brief A callback function to output the power mode of the device + * Once done, it is stored in the sl->family_data to avoid doing the test + * during data read + * @return 0 : device parasite powered + * 1 : device externally powered + * -xx : xx is kernel error code + */ +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-----------------------------*/ -- 2.25.4