diff options
author | Akira Shimahara <akira215corp@gmail.com> | 2020-05-11 22:37:25 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2020-05-15 16:28:59 +0200 |
commit | 45d457a4cf24455eefd076a01a3d86414fc2ff1e (patch) | |
tree | 8ad2ca133c103d15ee6c960c333bb22148fd6768 | |
parent | 308bdb94de0c1abe7eac5193f58638b8aeaddf4b (diff) | |
download | linux-stable-45d457a4cf24455eefd076a01a3d86414fc2ff1e.tar.gz linux-stable-45d457a4cf24455eefd076a01a3d86414fc2ff1e.tar.bz2 linux-stable-45d457a4cf24455eefd076a01a3d86414fc2ff1e.zip |
w1_therm: adding eeprom sysfs entry
The driver implement 2 hardware functions to access device RAM:
* copy_scratchpad
* recall_scratchpad
They act according to device specifications.
As EEPROM operations are not device dependent (all w1_therm can perform
EEPROM read/write operation following the same protocol), it is removed
from device families structures.
Updating Documentation/ABI/testing/sysfs-driver-w1_therm accordingly.
Signed-off-by: Akira Shimahara <akira215corp@gmail.com>
Link: https://lore.kernel.org/r/20200511203725.410844-1-akira215corp@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | Documentation/ABI/testing/sysfs-driver-w1_therm | 14 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_therm.c | 175 |
2 files changed, 132 insertions, 57 deletions
diff --git a/Documentation/ABI/testing/sysfs-driver-w1_therm b/Documentation/ABI/testing/sysfs-driver-w1_therm index 7ed95e9e0b64..8b7ee89ac6ce 100644 --- a/Documentation/ABI/testing/sysfs-driver-w1_therm +++ b/Documentation/ABI/testing/sysfs-driver-w1_therm @@ -1,3 +1,17 @@ +What: /sys/bus/w1/devices/.../eeprom +Date: May 2020 +Contact: Akira Shimahara <akira215corp@gmail.com> +Description: + (WO) writing that file will either trigger a save of the + device data to its embedded EEPROM, either restore data + embedded in device EEPROM. Be aware that devices support + limited EEPROM writing cycles (typical 50k) + * 'save': save device RAM to EEPROM + * 'restore': restore EEPROM data in device RAM +Users: any user space application which wants to communicate with + w1_term device + + What: /sys/bus/w1/devices/.../ext_power Date: May 2020 Contact: Akira Shimahara <akira215corp@gmail.com> diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index 7ab8a141ce6e..1ba07b0833fa 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -43,12 +43,21 @@ static int w1_strong_pullup = 1; module_param_named(strong_pullup, w1_strong_pullup, int, 0); +/* This command should be in public header w1.h but is not */ +#define W1_RECALL_EEPROM 0xB8 + /* Nb of try for an operation */ #define W1_THERM_MAX_TRY 5 /* ms delay to retry bus mutex */ #define W1_THERM_RETRY_DELAY 20 +/* delay in ms to write in EEPROM */ +#define W1_THERM_EEPROM_WRITE_DELAY 10 + +#define EEPROM_CMD_WRITE "save" /* cmd for write eeprom sysfs */ +#define EEPROM_CMD_READ "restore" /* cmd for read eeprom sysfs */ + /* Helpers Macros */ /* @@ -86,7 +95,6 @@ module_param_named(strong_pullup, w1_strong_pullup, int, 0); * @convert: pointer to the device conversion function * @set_resolution: pointer to the device set_resolution function * @get_resolution: pointer to the device get_resolution function - * @eeprom: pointer to eeprom function */ struct w1_therm_family_converter { u8 broken; @@ -95,7 +103,6 @@ struct w1_therm_family_converter { int (*convert)(u8 rom[9]); int (*set_resolution)(struct w1_slave *sl, int val); int (*get_resolution)(struct w1_slave *sl); - int (*eeprom)(struct device *device); }; /** @@ -166,6 +173,22 @@ static int read_scratchpad(struct w1_slave *sl, struct therm_info *info); static int write_scratchpad(struct w1_slave *sl, const u8 *data, u8 nb_bytes); /** + * copy_scratchpad() - Copy the content of scratchpad in device EEPROM + * @sl: slave involved + * + * Return: 0 if success, -kernel error code otherwise + */ +static int copy_scratchpad(struct w1_slave *sl); + +/** + * recall_eeprom() - Restore EEPROM data to device RAM + * @sl: slave involved + * + * Return: 0 if success, -kernel error code otherwise + */ +static int recall_eeprom(struct w1_slave *sl); + +/** * read_powermode() - Query the power mode of the slave * @sl: slave to retrieve the power mode * @@ -199,12 +222,16 @@ static ssize_t resolution_show(struct device *device, static ssize_t resolution_store(struct device *device, struct device_attribute *attr, const char *buf, size_t size); +static ssize_t eeprom_store(struct device *device, + struct device_attribute *attr, const char *buf, size_t size); + /* Attributes declarations */ static DEVICE_ATTR_RW(w1_slave); static DEVICE_ATTR_RO(w1_seq); static DEVICE_ATTR_RO(ext_power); static DEVICE_ATTR_RW(resolution); +static DEVICE_ATTR_WO(eeprom); /* Interface Functions declaration */ @@ -234,12 +261,14 @@ static struct attribute *w1_therm_attrs[] = { &dev_attr_w1_slave.attr, &dev_attr_ext_power.attr, &dev_attr_resolution.attr, + &dev_attr_eeprom.attr, NULL, }; static struct attribute *w1_ds18s20_attrs[] = { &dev_attr_w1_slave.attr, &dev_attr_ext_power.attr, + &dev_attr_eeprom.attr, NULL, }; @@ -248,6 +277,7 @@ static struct attribute *w1_ds28ea00_attrs[] = { &dev_attr_w1_seq.attr, &dev_attr_ext_power.attr, &dev_attr_resolution.attr, + &dev_attr_eeprom.attr, NULL, }; @@ -359,9 +389,6 @@ static struct w1_family w1_therm_family_DS1825 = { /* Device dependent func */ -/* write configuration to eeprom */ -static inline int w1_therm_eeprom(struct device *device); - static inline int w1_DS18B20_write_data(struct w1_slave *sl, const u8 *data) { @@ -477,35 +504,30 @@ static struct w1_therm_family_converter w1_therm_families[] = { .convert = w1_DS18S20_convert_temp, .set_resolution = NULL, /* no config register */ .get_resolution = NULL, /* no config register */ - .eeprom = w1_therm_eeprom }, { .f = &w1_therm_family_DS1822, .convert = w1_DS18B20_convert_temp, .set_resolution = w1_DS18B20_set_resolution, .get_resolution = w1_DS18B20_get_resolution, - .eeprom = w1_therm_eeprom }, { .f = &w1_therm_family_DS18B20, .convert = w1_DS18B20_convert_temp, .set_resolution = w1_DS18B20_set_resolution, .get_resolution = w1_DS18B20_get_resolution, - .eeprom = w1_therm_eeprom }, { .f = &w1_therm_family_DS28EA00, .convert = w1_DS18B20_convert_temp, .set_resolution = w1_DS18B20_set_resolution, .get_resolution = w1_DS18B20_get_resolution, - .eeprom = w1_therm_eeprom }, { .f = &w1_therm_family_DS1825, .convert = w1_DS18B20_convert_temp, .set_resolution = w1_DS18B20_set_resolution, .get_resolution = w1_DS18B20_get_resolution, - .eeprom = w1_therm_eeprom } }; @@ -838,75 +860,94 @@ error: return ret; } -static inline int w1_therm_eeprom(struct device *device) +static int copy_scratchpad(struct w1_slave *sl) { - struct w1_slave *sl = dev_to_w1_slave(device); - struct w1_master *dev = sl->master; - u8 rom[9], external_power; - int ret, max_trying = 10; - u8 *family_data = sl->family_data; + struct w1_master *dev_master = sl->master; + int max_trying = W1_THERM_MAX_TRY; + int t_write, ret = -ENODEV; + bool strong_pullup; - if (!sl->family_data) { - ret = -ENODEV; + if (!sl->family_data) goto error; - } + + t_write = W1_THERM_EEPROM_WRITE_DELAY; + strong_pullup = (w1_strong_pullup == 2 || + (!SLAVE_POWERMODE(sl) && + w1_strong_pullup)); /* prevent the slave from going away in sleep */ - atomic_inc(THERM_REFCNT(family_data)); + atomic_inc(THERM_REFCNT(sl->family_data)); - ret = mutex_lock_interruptible(&dev->bus_mutex); - if (ret != 0) + if (!bus_mutex_lock(&dev_master->bus_mutex)) { + ret = -EAGAIN; /* Didn't acquire the mutex */ goto dec_refcnt; + } - memset(rom, 0, sizeof(rom)); - - while (max_trying--) { + while (max_trying-- && ret) { /* ret should be 0 */ + /* safe version to select slave */ if (!reset_select_slave(sl)) { - unsigned int tm = 10; unsigned long sleep_rem; - /* check if in parasite mode */ - w1_write_8(dev, W1_READ_PSUPPLY); - external_power = w1_read_8(dev); - - if (reset_select_slave(sl)) - continue; - - /* 10ms strong pullup/delay after the copy command */ - if (w1_strong_pullup == 2 || - (!external_power && w1_strong_pullup)) - w1_next_pullup(dev, tm); + /* 10ms strong pullup (or delay) after the convert */ + if (strong_pullup) + w1_next_pullup(dev_master, t_write); - w1_write_8(dev, W1_COPY_SCRATCHPAD); - - if (external_power) { - mutex_unlock(&dev->bus_mutex); + w1_write_8(dev_master, W1_COPY_SCRATCHPAD); - sleep_rem = msleep_interruptible(tm); - if (sleep_rem != 0) { - ret = -EINTR; - goto dec_refcnt; - } - - ret = mutex_lock_interruptible(&dev->bus_mutex); - if (ret != 0) - goto dec_refcnt; - } else if (!w1_strong_pullup) { - sleep_rem = msleep_interruptible(tm); + if (strong_pullup) { + sleep_rem = msleep_interruptible(t_write); if (sleep_rem != 0) { ret = -EINTR; goto mt_unlock; } } - - break; + ret = 0; } + } mt_unlock: - mutex_unlock(&dev->bus_mutex); + mutex_unlock(&dev_master->bus_mutex); dec_refcnt: - atomic_dec(THERM_REFCNT(family_data)); + atomic_dec(THERM_REFCNT(sl->family_data)); +error: + return ret; +} + +static int recall_eeprom(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) { /* ret should be 0 */ + /* safe version to select slave */ + if (!reset_select_slave(sl)) { + + w1_write_8(dev_master, W1_RECALL_EEPROM); + + ret = 1; /* Slave will pull line to 0 */ + while (ret) + ret = 1 - w1_touch_bit(dev_master, 1); + } + + } + + mutex_unlock(&dev_master->bus_mutex); + +dec_refcnt: + atomic_dec(THERM_REFCNT(sl->family_data)); error: return ret; } @@ -1006,7 +1047,7 @@ static ssize_t w1_slave_store(struct device *device, } if (val == 0) /* val=0 : trigger a EEPROM save */ - ret = SLAVE_SPECIFIC_FUNC(sl)->eeprom(device); + ret = copy_scratchpad(sl); else { if (SLAVE_SPECIFIC_FUNC(sl)->set_resolution) ret = SLAVE_SPECIFIC_FUNC(sl)->set_resolution(sl, val); @@ -1105,6 +1146,26 @@ static ssize_t resolution_store(struct device *device, return size; } +static ssize_t eeprom_store(struct device *device, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct w1_slave *sl = dev_to_w1_slave(device); + int ret = -EINVAL; /* Invalid argument */ + + if (size == sizeof(EEPROM_CMD_WRITE)) { + if (!strncmp(buf, EEPROM_CMD_WRITE, sizeof(EEPROM_CMD_WRITE)-1)) + ret = copy_scratchpad(sl); + } else if (size == sizeof(EEPROM_CMD_READ)) { + if (!strncmp(buf, EEPROM_CMD_READ, sizeof(EEPROM_CMD_READ)-1)) + ret = recall_eeprom(sl); + } + + if (ret) + dev_info(device, "%s: error in process %d\n", __func__, ret); + + return size; +} + #if IS_REACHABLE(CONFIG_HWMON) static int w1_read_temp(struct device *device, u32 attr, int channel, long *val) |