diff options
author | Álvaro Fernández Rojas <noltari@gmail.com> | 2021-02-18 18:04:33 +0100 |
---|---|---|
committer | Álvaro Fernández Rojas <noltari@gmail.com> | 2021-02-19 07:17:21 +0100 |
commit | 62b7f5931c54e96fca56dd8761b0e466d355c881 (patch) | |
tree | 1258b392752379833a075df006c2f6d7ac4be51d /target/linux/bcm27xx/patches-5.4/950-0773-w1_therm-adding-bulk-read-support-to-trigger-multipl.patch | |
parent | 76d1168d0d4b9d76e2ad78c0fc6b255561deb284 (diff) | |
download | upstream-62b7f5931c54e96fca56dd8761b0e466d355c881.tar.gz upstream-62b7f5931c54e96fca56dd8761b0e466d355c881.tar.bz2 upstream-62b7f5931c54e96fca56dd8761b0e466d355c881.zip |
bcm27xx: import latest patches from the RPi foundation
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>
(cherry-picked from commit f07e572f64)
Diffstat (limited to 'target/linux/bcm27xx/patches-5.4/950-0773-w1_therm-adding-bulk-read-support-to-trigger-multipl.patch')
-rw-r--r-- | target/linux/bcm27xx/patches-5.4/950-0773-w1_therm-adding-bulk-read-support-to-trigger-multipl.patch | 587 |
1 files changed, 587 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-5.4/950-0773-w1_therm-adding-bulk-read-support-to-trigger-multipl.patch b/target/linux/bcm27xx/patches-5.4/950-0773-w1_therm-adding-bulk-read-support-to-trigger-multipl.patch new file mode 100644 index 0000000000..ad641e1e98 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0773-w1_therm-adding-bulk-read-support-to-trigger-multipl.patch @@ -0,0 +1,587 @@ +From e6585a1d299375740f95f30027b8b3f4b34e7548 Mon Sep 17 00:00:00 2001 +From: Akira Shimahara <akira215corp@gmail.com> +Date: Mon, 11 May 2020 22:38:20 +0200 +Subject: [PATCH] w1_therm: adding bulk read support to trigger + multiple conversion on bus + +commit 57c76221d5af648c8355a55c09b050c5d8d38189 upstream. + +Adding bulk read support: +Sending a 'trigger' command in the dedicated sysfs entry of 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. + +The behavior of the sysfs reading temperature on the device is as follow: + * If no bulk read pending, trigger a conversion on the device, wait for + the conversion to be done, read the temperature in device RAM + * If a bulk read has been trigger, access directly the device RAM +This behavior is the same on the 2 sysfs entries ('temperature' and +'w1_slave'). + +Reading the therm_bulk_read sysfs give the status of bulk operations: + * '-1': conversion in progress on at least 1 sensor + * '1': conversion complete but at least one sensor has not been read yet + * '0': no bulk operation. Reading temperature on ecah device will trigger +a conversion + +As not all devices support bulk read feature, it has been added in device +family structure. + +The attribute is set at master level as soon as a supporting device is +discover. It is removed when the last supported device leave the bus. +The count of supported device is kept with the static counter +bulk_read_device_counter. + +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 accordingly. + +Signed-off-by: Akira Shimahara <akira215corp@gmail.com> +Link: https://lore.kernel.org/r/20200511203820.411483-1-akira215corp@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + .../ABI/testing/sysfs-driver-w1_therm | 36 ++- + Documentation/w1/slaves/w1_therm.rst | 50 +++- + drivers/w1/slaves/w1_therm.c | 251 +++++++++++++++++- + 3 files changed, 322 insertions(+), 15 deletions(-) + +--- a/Documentation/ABI/testing/sysfs-driver-w1_therm ++++ b/Documentation/ABI/testing/sysfs-driver-w1_therm +@@ -62,9 +62,16 @@ Date: May 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 + +@@ -85,4 +92,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: May 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 +--- 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 configura + 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" +--- a/drivers/w1/slaves/w1_therm.c ++++ b/drivers/w1/slaves/w1_therm.c +@@ -43,6 +43,9 @@ + static int w1_strong_pullup = 1; + module_param_named(strong_pullup, w1_strong_pullup, int, 0); + ++/* Counter for devices supporting bulk reading */ ++static u16 bulk_read_device_counter; /* =0 as per C standard */ ++ + /* This command should be in public header w1.h but is not */ + #define W1_RECALL_EEPROM 0xB8 + +@@ -57,6 +60,7 @@ module_param_named(strong_pullup, w1_str + + #define EEPROM_CMD_WRITE "save" /* cmd for write eeprom sysfs */ + #define EEPROM_CMD_READ "restore" /* cmd for read eeprom sysfs */ ++#define BULK_TRIGGER_CMD "trigger" /* cmd to trigger a bulk read */ + + #define MIN_TEMP -55 /* min temperature that can be mesured */ + #define MAX_TEMP 125 /* max temperature that can be mesured */ +@@ -84,6 +88,15 @@ module_param_named(strong_pullup, w1_str + #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) +@@ -100,6 +113,7 @@ module_param_named(strong_pullup, w1_str + * @set_resolution: pointer to the device set_resolution function + * @get_resolution: pointer to the device get_resolution function + * @write_data: pointer to the device writing function (2 or 3 bytes) ++ * @bulk_read: true if device family support bulk read, false otherwise + */ + struct w1_therm_family_converter { + u8 broken; +@@ -110,6 +124,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; + }; + + /** +@@ -120,6 +135,7 @@ struct w1_therm_family_converter { + * 0 device parasite powered, + * -x error or undefined + * @resolution: current device resolution ++ * @convert_triggered: conversion state of the device + * @specific_functions: pointer to struct of device specific function + */ + struct w1_therm_family_data { +@@ -127,6 +143,7 @@ struct w1_therm_family_data { + atomic_t refcnt; + int external_powered; + int resolution; ++ int convert_triggered; + struct w1_therm_family_converter *specific_functions; + }; + +@@ -218,6 +235,18 @@ static int recall_eeprom(struct w1_slave + */ + static int read_powermode(struct w1_slave *sl); + ++/** ++ * trigger_bulk_read() - function to trigger a bulk read on the bus ++ * @dev_master: the device master of the bus ++ * ++ * Send a SKIP ROM follow by a CONVERT T commmand on the bus. ++ * It also set the status flag in each slave &struct w1_therm_family_data ++ * to signal that a conversion is in progress. ++ * ++ * Return: 0 if success, -kernel error code otherwise ++ */ ++static int trigger_bulk_read(struct w1_master *dev_master); ++ + /* Sysfs interface declaration */ + + static ssize_t w1_slave_show(struct device *device, +@@ -250,6 +279,12 @@ static ssize_t alarms_store(struct devic + static ssize_t alarms_show(struct device *device, + struct device_attribute *attr, char *buf); + ++static ssize_t therm_bulk_read_store(struct device *device, ++ struct device_attribute *attr, const char *buf, size_t size); ++ ++static ssize_t therm_bulk_read_show(struct device *device, ++ struct device_attribute *attr, char *buf); ++ + /* Attributes declarations */ + + static DEVICE_ATTR_RW(w1_slave); +@@ -260,6 +295,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 declaration */ + + /** +@@ -572,6 +609,7 @@ static struct w1_therm_family_converter + .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, +@@ -580,6 +618,7 @@ static struct w1_therm_family_converter + .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, +@@ -588,6 +627,7 @@ static struct w1_therm_family_converter + .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, +@@ -596,6 +636,7 @@ static struct w1_therm_family_converter + .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, +@@ -604,6 +645,7 @@ static struct w1_therm_family_converter + .set_resolution = w1_DS18B20_set_resolution, + .get_resolution = w1_DS18B20_get_resolution, + .write_data = w1_DS18B20_write_data, ++ .bulk_read = true + } + }; + +@@ -658,6 +700,23 @@ static inline bool bus_mutex_lock(struct + } + + /** ++ * support_bulk_read() - check if slave support bulk read ++ * @sl: device to check the ability ++ * ++ * Return: true if bulk read is supported, false if not or error ++ */ ++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 */ ++} ++ ++/** + * conversion_time() - get the Tconv for the slave + * @sl: device to get the conversion time + * +@@ -741,6 +800,24 @@ static int w1_therm_add_slave(struct w1_ + /* save this pointer to the device structure */ + 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); + +@@ -763,6 +840,9 @@ static int w1_therm_add_slave(struct w1_ + } + } + ++ /* Finally initialize convert_triggered flag */ ++ SLAVE_CONVERT_TRIGGERED(sl) = 0; ++ + return 0; + } + +@@ -770,6 +850,14 @@ static void w1_therm_remove_slave(struct + { + 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)); +@@ -1084,6 +1172,96 @@ 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; ++} ++ + /* Sysfs Interface definition */ + + static ssize_t w1_slave_show(struct device *device, +@@ -1095,7 +1273,20 @@ static ssize_t w1_slave_show(struct devi + 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, +@@ -1176,7 +1367,20 @@ static ssize_t temperature_show(struct d + 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, +@@ -1412,6 +1616,49 @@ 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) |