lkml.org 
[lkml]   [2020]   [Apr]   [29]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v3 5/5] w1_therm: adding bulk read support. Updating docs in w1_therm.rst and sysfs-driver-w1_therm
    Date
    Patch for enhacement of w1_therm module. Adding bulk read support.
    Sending a 'trigger' command in the dedicated sysfs entry of the bus master
    device send a conversion command for all the slaves on the bus. The sysfs
    entry is added as soon as at least one device supporting this feature
    is detected on the bus.

    A strong pull up is apply on the line if at least one device required it.
    The duration of the pull up is the max time required by a device on the
    line, which depends on the resolution settings of each device. The strong
    pull up could be adjust with the a module parameter.

    Updating documentation in Documentation/ABI/testing/sysfs-driver-w1_therm
    and Documentation/w1/slaves/w1_therm.rst

    Signed-off-by: Akira Shimahara <akira215corp@gmail.com>
    ---
    .../ABI/testing/sysfs-driver-w1_therm | 36 ++-
    Documentation/w1/slaves/w1_therm.rst | 50 +++-
    drivers/w1/slaves/w1_therm.c | 217 +++++++++++++++++-
    drivers/w1/slaves/w1_therm.h | 47 ++++
    4 files changed, 335 insertions(+), 15 deletions(-)

    diff --git a/Documentation/ABI/testing/sysfs-driver-w1_therm b/Documentation/ABI/testing/sysfs-driver-w1_therm
    index 39488a4..1f911ed 100644
    --- a/Documentation/ABI/testing/sysfs-driver-w1_therm
    +++ b/Documentation/ABI/testing/sysfs-driver-w1_therm
    @@ -61,9 +61,16 @@ Date: Apr 2020
    Contact: Akira Shimahara <akira215corp@gmail.com>
    Description:
    (RO) return the temperature in 1/1000 degC.
    - Note that the conversion duration depend on the resolution (if
    - device support this feature). It takes 94ms in 9bits
    - resolution, 750ms for 12bits.
    + * If a bulk read has been triggered, it will directly
    + return the temperature computed when the bulk read
    + occurred, if available. If not yet available, nothing
    + is returned (a debug kernel message is sent), you
    + should retry later on.
    + * If no bulk read has been triggered, it will trigger
    + a conversion and send the result. Note that the
    + conversion duration depend on the resolution (if
    + device support this feature). It takes 94ms in 9bits
    + resolution, 750ms for 12bits.
    Users: any user space application which wants to communicate with
    w1_term device

    @@ -84,4 +91,25 @@ Description:
    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
    + w1_term device
    +
    +
    +What: /sys/bus/w1/devices/w1_bus_masterXX/therm_bulk_read
    +Date: Apr 2020
    +Contact: Akira Shimahara <akira215corp@gmail.com>
    +Description:
    + (RW) trigger a bulk read conversion. read the status
    + *read*:
    + * `-1`: conversion in progress on at least 1 sensor
    + * `1` : conversion complete but at least one sensor
    + value has not been read yet
    + * `0` : no bulk operation. Reading temperature will
    + trigger a conversion on each device
    + *write*: `trigger`: trigger a bulk read on all supporting
    + devices on the bus
    + Note that if a bulk read is sent but one sensor is not read
    + immediately, the next access to temperature on this device
    + will return the temperature measured at the time of issue
    + of the bulk read command (not the current temperature).
    +Users: any user space application which wants to communicate with
    + w1_term device
    diff --git a/Documentation/w1/slaves/w1_therm.rst b/Documentation/w1/slaves/w1_therm.rst
    index 82e8ffe..06eaff1 100644
    --- a/Documentation/w1/slaves/w1_therm.rst
    +++ b/Documentation/w1/slaves/w1_therm.rst
    @@ -26,20 +26,31 @@ W1_THERM_DS1825 0x3B
    W1_THERM_DS28EA00 0x42
    ==================== ====

    -Support is provided through the sysfs w1_slave file. Each open and
    +Support is provided through the sysfs w1_slave file. Each open and
    read sequence will initiate a temperature conversion then provide two
    -lines of ASCII output. The first line contains the nine hex bytes
    +lines of ASCII output. The first line contains the nine hex bytes
    read along with a calculated crc value and YES or NO if it matched.
    -If the crc matched the returned values are retained. The second line
    +If the crc matched the returned values are retained. The second line
    displays the retained values along with a temperature in millidegrees
    Centigrade after t=.

    -Parasite powered devices are limited to one slave performing a
    -temperature conversion at a time. If none of the devices are parasite
    -powered it would be possible to convert all the devices at the same
    -time and then go back to read individual sensors. That isn't
    -currently supported. The driver also doesn't support reduced
    -precision (which would also reduce the conversion time) when reading values.
    +Alternatively, temperature can be read using temperature sysfs, it
    +return only temperature in millidegrees Centigrade.
    +
    +A bulk read of all devices on the bus could be done writing 'trigger'
    +in the therm_bulk_read sysfs entry at w1_bus_master level. This will
    +sent the convert command on all devices on the bus, and if parasite
    +powered devices are detected on the bus (and strong pullup is enable
    +in the module), it will drive the line high during the longer conversion
    +time required by parasited powered device on the line. Reading
    +therm_bulk_read will return 0 if no bulk conversion pending,
    +-1 if at least one sensor still in conversion, 1 if conversion is complete
    +but at least one sensor value has not been read yet. Result temperature is
    +then accessed by reading the temperature sysfs entry of each device, which
    +may return empty if conversion is still in progress. Note that if a bulk
    +read is sent but one sensor is not read immediately, the next access to
    +temperature on this device will return the temperature measured at the
    +time of issue of the bulk read command (not the current temperature).

    Writing a value between 9 and 12 to the sysfs w1_slave file will change the
    precision of the sensor for the next readings. This value is in (volatile)
    @@ -49,6 +60,27 @@ To store the current precision configuration into EEPROM, the value 0
    has to be written to the sysfs w1_slave file. Since the EEPROM has a limited
    amount of writes (>50k), this command should be used wisely.

    +Alternatively, resolution can be set or read (value from 9 to 12) using the
    +dedicated resolution sysfs entry on each device. This sysfs entry is not
    +present for devices not supporting this feature. Driver will adjust the
    +correct conversion time for each device regarding to its resolution setting.
    +In particular, strong pullup will be applied if required during the conversion
    +duration.
    +
    +The write-only sysfs entry eeprom is an alternative for EEPROM operations:
    + * `save`: will save device RAM to EEPROM
    + * `restore`: will restore EEPROM data in device RAM.
    +
    +ext_power syfs entry allow tho check the power status of each device.
    + * `0`: device parasite powered
    + * `1`: device externally powered
    +
    +sysfs alarms allow read or write TH and TL (Temperature High an Low) alarms.
    +Values shall be space separated and in the device range (typical -55 degC
    +to 125 degC). Values are integer as they are store in a 8bit register in
    +the device. Lowest value is automatically put to TL.Once set, alarms could
    +be search at master level.
    +
    The module parameter strong_pullup can be set to 0 to disable the
    strong pullup, 1 to enable autodetection or 2 to force strong pullup.
    In case of autodetection, the driver will use the "READ POWER SUPPLY"
    diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c
    index db75048..42b21fe 100644
    --- a/drivers/w1/slaves/w1_therm.c
    +++ b/drivers/w1/slaves/w1_therm.c
    @@ -67,6 +67,15 @@ module_param_named(strong_pullup, w1_strong_pullup, int, 0);
    * . 'restore' : restore EEPROM data in device RAM
    * (device do that automatically on power-up)
    *
    + * therm_bulk_read (RW): Attribute at master level
    + * . 'trigger' : trigger a bulk read on all supporting device on the bus
    + * read value:
    + * . -1 : conversion is in progress in 1 or more sensor
    + * . 1 : conversion complete but at least one sensor has not been read
    + * . 0 : no bulk operation. Reading temp will trigger a conversion
    + * caveat : if a bulk read is sent but one sensor is not read immediately,
    + * the next access to temperature will return the temperature measured
    + * at the time of issue of the bulk read command
    *
    * alarms (RW) : read TH and TL (Temperature High an Low) alarms
    * Values shall be space separated and in the device range
    @@ -225,6 +234,7 @@ static struct w1_therm_family_converter w1_therm_families[] = {
    .set_resolution = NULL, // no config register
    .get_resolution = NULL, // no config register
    .write_data = w1_DS18S20_write_data,
    + .bulk_read = true
    },
    {
    .f = &w1_therm_family_DS1822,
    @@ -233,6 +243,7 @@ static struct w1_therm_family_converter w1_therm_families[] = {
    .set_resolution = w1_DS18B20_set_resolution,
    .get_resolution = w1_DS18B20_get_resolution,
    .write_data = w1_DS18B20_write_data,
    + .bulk_read = true
    },
    {
    .f = &w1_therm_family_DS18B20,
    @@ -241,6 +252,7 @@ static struct w1_therm_family_converter w1_therm_families[] = {
    .set_resolution = w1_DS18B20_set_resolution,
    .get_resolution = w1_DS18B20_get_resolution,
    .write_data = w1_DS18B20_write_data,
    + .bulk_read = true
    },
    {
    .f = &w1_therm_family_DS28EA00,
    @@ -249,6 +261,7 @@ static struct w1_therm_family_converter w1_therm_families[] = {
    .set_resolution = w1_DS18B20_set_resolution,
    .get_resolution = w1_DS18B20_get_resolution,
    .write_data = w1_DS18B20_write_data,
    + .bulk_read = false
    },
    {
    .f = &w1_therm_family_DS1825,
    @@ -257,6 +270,7 @@ static struct w1_therm_family_converter w1_therm_families[] = {
    .set_resolution = w1_DS18B20_set_resolution,
    .get_resolution = w1_DS18B20_get_resolution,
    .write_data = w1_DS18B20_write_data,
    + .bulk_read = true
    }
    };

    @@ -420,6 +434,18 @@ static inline bool bus_mutex_lock(struct mutex *lock)
    return true;
    }

    +
    +static inline bool bulk_read_support(struct w1_slave *sl)
    +{
    + if (SLAVE_SPECIFIC_FUNC(sl))
    + return SLAVE_SPECIFIC_FUNC(sl)->bulk_read;
    +
    + dev_info(&sl->dev,
    + "%s: Device not supported by the driver\n", __func__);
    +
    + return false; /* No device family */
    +}
    +
    static inline int conversion_time(struct w1_slave *sl)
    {
    if (SLAVE_SPECIFIC_FUNC(sl))
    @@ -469,6 +495,24 @@ static int w1_therm_add_slave(struct w1_slave *sl)
    return -ENODEV;
    }
    SLAVE_SPECIFIC_FUNC(sl) = sl_family_conv;
    +
    + if (bulk_read_support(sl)) {
    + /* add the sys entry to trigger bulk_read
    + * at master level only the 1st time
    + */
    + if (!bulk_read_device_counter) {
    + int err = device_create_file(&sl->master->dev,
    + &dev_attr_therm_bulk_read);
    +
    + if (err)
    + dev_warn(&sl->dev,
    + "%s: Device has been added, but bulk read is unavailable. err=%d\n",
    + __func__, err);
    + }
    + /* Increment the counter */
    + bulk_read_device_counter++;
    + }
    +
    /* Getting the power mode of the device {external, parasite}*/
    SLAVE_POWERMODE(sl) = read_powermode(sl);

    @@ -491,6 +535,9 @@ static int w1_therm_add_slave(struct w1_slave *sl)
    }
    }

    + /* Finally initialize convert_triggered flag */
    + SLAVE_CONVERT_TRIGGERED(sl) = 0;
    +
    return 0;
    }

    @@ -498,6 +545,14 @@ static void w1_therm_remove_slave(struct w1_slave *sl)
    {
    int refcnt = atomic_sub_return(1, THERM_REFCNT(sl->family_data));

    + if (bulk_read_support(sl)) {
    + bulk_read_device_counter--;
    + /* Delete the entry if no more device support the feature */
    + if (!bulk_read_device_counter)
    + device_remove_file(&sl->master->dev,
    + &dev_attr_therm_bulk_read);
    + }
    +
    while (refcnt) {
    msleep(1000);
    refcnt = atomic_read(THERM_REFCNT(sl->family_data));
    @@ -813,6 +868,94 @@ error:
    return ret;
    }

    +static int trigger_bulk_read(struct w1_master *dev_master)
    +{
    + struct w1_slave *sl = NULL; /* used to iterate through slaves */
    + int max_trying = W1_THERM_MAX_TRY;
    + int t_conv = 0;
    + int ret = -ENODEV;
    + bool strong_pullup = false;
    +
    + /* Check whether there are parasite powered device on the bus,
    + * and compute duration of conversion for these devices
    + * so we can apply a strong pullup if required
    + */
    + list_for_each_entry(sl, &dev_master->slist, w1_slave_entry) {
    + if (!sl->family_data)
    + goto error;
    + if (bulk_read_support(sl)) {
    + int t_cur = conversion_time(sl);
    +
    + t_conv = t_cur > t_conv ? t_cur : t_conv;
    + strong_pullup = strong_pullup ||
    + (w1_strong_pullup == 2 ||
    + (!SLAVE_POWERMODE(sl) &&
    + w1_strong_pullup));
    + }
    + }
    +
    + /* t_conv is the max conversion time required on the bus
    + * If its 0, no device support the bulk read feature
    + */
    + if (!t_conv)
    + goto error;
    +
    + if (!bus_mutex_lock(&dev_master->bus_mutex)) {
    + ret = -EAGAIN; // Didn't acquire the mutex
    + goto error;
    + }
    +
    + while ((max_trying--) && (ret < 0)) { /* ret should be either 0 */
    +
    + if (!w1_reset_bus(dev_master)) { /* Just reset the bus */
    + unsigned long sleep_rem;
    +
    + w1_write_8(dev_master, W1_SKIP_ROM);
    +
    + if (strong_pullup) /* Apply pullup if required */
    + w1_next_pullup(dev_master, t_conv);
    +
    + w1_write_8(dev_master, W1_CONVERT_TEMP);
    +
    + /* set a flag to instruct that converT pending */
    + list_for_each_entry(sl,
    + &dev_master->slist, w1_slave_entry) {
    + if (bulk_read_support(sl))
    + SLAVE_CONVERT_TRIGGERED(sl) = -1;
    + }
    +
    + if (strong_pullup) { /*some device need pullup */
    + sleep_rem = msleep_interruptible(t_conv);
    + if (sleep_rem != 0) {
    + ret = -EINTR;
    + goto mt_unlock;
    + }
    + mutex_unlock(&dev_master->bus_mutex);
    + } else {
    + mutex_unlock(&dev_master->bus_mutex);
    + sleep_rem = msleep_interruptible(t_conv);
    + if (sleep_rem != 0) {
    + ret = -EINTR;
    + goto set_flag;
    + }
    + }
    + ret = 0;
    + goto set_flag;
    + }
    + }
    +
    +mt_unlock:
    + mutex_unlock(&dev_master->bus_mutex);
    +set_flag:
    + /* set a flag to register convsersion is done */
    + list_for_each_entry(sl, &dev_master->slist, w1_slave_entry) {
    + if (bulk_read_support(sl))
    + SLAVE_CONVERT_TRIGGERED(sl) = 1;
    + }
    +error:
    + return ret;
    +}
    +
    /*------------------------Interface sysfs--------------------------*/

    static ssize_t w1_slave_show(struct device *device,
    @@ -824,7 +967,20 @@ static ssize_t w1_slave_show(struct device *device,
    int ret, i;
    ssize_t c = PAGE_SIZE;

    - ret = convert_t(sl, &info);
    + if (bulk_read_support(sl)) {
    + if (SLAVE_CONVERT_TRIGGERED(sl) < 0) {
    + dev_dbg(device,
    + "%s: Conversion in progress, retry later\n",
    + __func__);
    + return 0;
    + } else if (SLAVE_CONVERT_TRIGGERED(sl) > 0) {
    + /* A bulk read has been issued, read the device RAM */
    + ret = read_scratchpad(sl, &info);
    + SLAVE_CONVERT_TRIGGERED(sl) = 0;
    + } else
    + ret = convert_t(sl, &info);
    + } else
    + ret = convert_t(sl, &info);

    if (ret < 0) {
    dev_dbg(device,
    @@ -905,7 +1061,20 @@ static ssize_t temperature_show(struct device *device,
    return 0; /* No device family */
    }

    - ret = convert_t(sl, &info);
    + if (bulk_read_support(sl)) {
    + if (SLAVE_CONVERT_TRIGGERED(sl) < 0) {
    + dev_dbg(device,
    + "%s: Conversion in progress, retry later\n",
    + __func__);
    + return 0;
    + } else if (SLAVE_CONVERT_TRIGGERED(sl) > 0) {
    + /* A bulk read has been issued, read the device RAM */
    + ret = read_scratchpad(sl, &info);
    + SLAVE_CONVERT_TRIGGERED(sl) = 0;
    + } else
    + ret = convert_t(sl, &info);
    + } else
    + ret = convert_t(sl, &info);

    if (ret < 0) {
    dev_dbg(device,
    @@ -1138,6 +1307,50 @@ free_m:

    return size;
    }
    +
    +static ssize_t therm_bulk_read_store(struct device *device,
    + struct device_attribute *attr, const char *buf, size_t size)
    +{
    + struct w1_master *dev_master = dev_to_w1_master(device);
    + int ret = -EINVAL; // Invalid argument
    +
    + if (size == sizeof(BULK_TRIGGER_CMD))
    + if (!strncmp(buf, BULK_TRIGGER_CMD,
    + sizeof(BULK_TRIGGER_CMD)-1))
    + ret = trigger_bulk_read(dev_master);
    +
    + if (ret)
    + dev_info(device,
    + "%s: unable to trigger a bulk read on the bus. err=%d\n",
    + __func__, ret);
    +
    + return size;
    +}
    +
    +static ssize_t therm_bulk_read_show(struct device *device,
    + struct device_attribute *attr, char *buf)
    +{
    + struct w1_master *dev_master = dev_to_w1_master(device);
    + struct w1_slave *sl = NULL;
    + int ret = 0;
    +
    + list_for_each_entry(sl, &dev_master->slist, w1_slave_entry) {
    + if (sl->family_data) {
    + if (bulk_read_support(sl)) {
    + if (SLAVE_CONVERT_TRIGGERED(sl) == -1) {
    + ret = -1;
    + goto show_result;
    + }
    + if (SLAVE_CONVERT_TRIGGERED(sl) == 1)
    + /* continue to check other slaves */
    + ret = 1;
    + }
    + }
    + }
    +show_result:
    + return sprintf(buf, "%d\n", ret);
    +}
    +
    #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 4f2dcf0..93b1760 100644
    --- a/drivers/w1/slaves/w1_therm.h
    +++ b/drivers/w1/slaves/w1_therm.h
    @@ -45,6 +45,9 @@
    #define MIN_TEMP -55 /* min temperature that can be mesured */
    #define MAX_TEMP 125 /* max temperature that can be mesured */

    +/* Counter for devices supporting bulk reading */
    +static u16 bulk_read_device_counter; // =0 as per C standard
    +
    /*----------------------------------Structs---------------------------------*/

    /**
    @@ -63,6 +66,7 @@ struct w1_therm_family_converter {
    int (*set_resolution)(struct w1_slave *sl, int val);
    int (*get_resolution)(struct w1_slave *sl);
    int (*write_data)(struct w1_slave *sl, const u8 *data);
    + bool bulk_read;
    };

    /**
    @@ -80,6 +84,7 @@ struct w1_therm_family_data {
    atomic_t refcnt;
    int external_powered;
    int resolution;
    + int convert_triggered;
    struct w1_therm_family_converter *specific_functions;
    };

    @@ -129,6 +134,15 @@ static inline int w1_DS18B20_get_resolution(struct w1_slave *sl);
    */
    #define SLAVE_RESOLUTION(sl) \
    (((struct w1_therm_family_data *)(sl->family_data))->resolution)
    +
    +/* return whether or not a converT command has been issued to the slave
    + * 0: no bulk read is pending
    + * -1: conversion is in progress
    + * 1: conversion done, result to be read
    + */
    +#define SLAVE_CONVERT_TRIGGERED(sl) \
    + (((struct w1_therm_family_data *)(sl->family_data))->convert_triggered)
    +
    /* return the address of the refcnt in the family data */
    #define THERM_REFCNT(family_data) \
    (&((struct w1_therm_family_data *)family_data)->refcnt)
    @@ -149,6 +163,12 @@ static struct w1_therm_family_converter *device_family(struct w1_slave *sl);
    */
    static inline bool bus_mutex_lock(struct mutex *lock);

    +/** support_bulk_read() check is device is supporting bulk read
    + * @param sl device to get the conversion time
    + * @return true : bulk read support, false : no support or error
    + */
    +static inline bool bulk_read_support(struct w1_slave *sl);
    +
    /** conversion_time() get the Tconv fo the device
    * @param sl device to get the conversion time
    * @return conversion time in ms, negative values kernel error code
    @@ -228,6 +248,15 @@ static int recall_eeprom(struct w1_slave *sl);
    * <0 kernel error code
    */
    static int read_powermode(struct w1_slave *sl);
    +
    +/** trigger_bulk_read()
    + * @brief send a SKIP ROM follow by a CONVERT T commmand
    + * on the bus. It also set a flag in each slave struct to signal
    + * @param dev_master the device master of the bus
    + * @return 0 if success, -kernel error code otherwise
    + */
    +static int trigger_bulk_read(struct w1_master *dev_master);
    +
    /*----------------------------Interface sysfs-------------------------------*/

    /** @brief A callback function to output the temperature Old way
    @@ -298,6 +327,22 @@ static ssize_t alarms_store(struct device *device,
    */
    static ssize_t alarms_show(struct device *device,
    struct device_attribute *attr, char *buf);
    +
    +/** @brief A callback function to trigger bulk read on the bus
    + * @param check BULK_TRIGGER_CMD macro
    + */
    +static ssize_t therm_bulk_read_store(struct device *device,
    + struct device_attribute *attr, const char *buf, size_t size);
    +
    +
    +/** @brief A callback function to check if bulk read is on progress
    + * @return -1 conversion in progress
    + * 1 conversion complete but not read on all sensors
    + * 0 no bulk operation pending
    + */
    +static ssize_t therm_bulk_read_show(struct device *device,
    + struct device_attribute *attr, char *buf);
    +
    /*-----------------------------Attributes declarations----------------------*/

    static DEVICE_ATTR_RW(w1_slave);
    @@ -308,6 +353,8 @@ static DEVICE_ATTR_RW(resolution);
    static DEVICE_ATTR_WO(eeprom);
    static DEVICE_ATTR_RW(alarms);

    +static DEVICE_ATTR_RW(therm_bulk_read); /* attribut at master level */
    +
    /*--------------------------Interface Functions-----------------------------*/

    /** w1_therm_add_slave() - Called each time a search discover a new device
    --
    2.25.4
    \
     
     \ /
      Last update: 2020-04-29 15:35    [W:3.688 / U:0.004 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site