Commit 45d457a4 authored by Akira Shimahara's avatar Akira Shimahara Committed by Greg Kroah-Hartman

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: default avatarAkira Shimahara <akira215corp@gmail.com>
Link: https://lore.kernel.org/r/20200511203725.410844-1-akira215corp@gmail.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 308bdb94
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 What: /sys/bus/w1/devices/.../ext_power
Date: May 2020 Date: May 2020
Contact: Akira Shimahara <akira215corp@gmail.com> Contact: Akira Shimahara <akira215corp@gmail.com>
......
...@@ -43,12 +43,21 @@ ...@@ -43,12 +43,21 @@
static int w1_strong_pullup = 1; static int w1_strong_pullup = 1;
module_param_named(strong_pullup, w1_strong_pullup, int, 0); 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 */ /* Nb of try for an operation */
#define W1_THERM_MAX_TRY 5 #define W1_THERM_MAX_TRY 5
/* ms delay to retry bus mutex */ /* ms delay to retry bus mutex */
#define W1_THERM_RETRY_DELAY 20 #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 */ /* Helpers Macros */
/* /*
...@@ -86,7 +95,6 @@ module_param_named(strong_pullup, w1_strong_pullup, int, 0); ...@@ -86,7 +95,6 @@ module_param_named(strong_pullup, w1_strong_pullup, int, 0);
* @convert: pointer to the device conversion function * @convert: pointer to the device conversion function
* @set_resolution: pointer to the device set_resolution function * @set_resolution: pointer to the device set_resolution function
* @get_resolution: pointer to the device get_resolution function * @get_resolution: pointer to the device get_resolution function
* @eeprom: pointer to eeprom function
*/ */
struct w1_therm_family_converter { struct w1_therm_family_converter {
u8 broken; u8 broken;
...@@ -95,7 +103,6 @@ struct w1_therm_family_converter { ...@@ -95,7 +103,6 @@ struct w1_therm_family_converter {
int (*convert)(u8 rom[9]); int (*convert)(u8 rom[9]);
int (*set_resolution)(struct w1_slave *sl, int val); int (*set_resolution)(struct w1_slave *sl, int val);
int (*get_resolution)(struct w1_slave *sl); int (*get_resolution)(struct w1_slave *sl);
int (*eeprom)(struct device *device);
}; };
/** /**
...@@ -165,6 +172,22 @@ static int read_scratchpad(struct w1_slave *sl, struct therm_info *info); ...@@ -165,6 +172,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); 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 * read_powermode() - Query the power mode of the slave
* @sl: slave to retrieve the power mode * @sl: slave to retrieve the power mode
...@@ -199,12 +222,16 @@ static ssize_t resolution_show(struct device *device, ...@@ -199,12 +222,16 @@ static ssize_t resolution_show(struct device *device,
static ssize_t resolution_store(struct device *device, static ssize_t resolution_store(struct device *device,
struct device_attribute *attr, const char *buf, size_t size); 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 */ /* Attributes declarations */
static DEVICE_ATTR_RW(w1_slave); static DEVICE_ATTR_RW(w1_slave);
static DEVICE_ATTR_RO(w1_seq); static DEVICE_ATTR_RO(w1_seq);
static DEVICE_ATTR_RO(ext_power); static DEVICE_ATTR_RO(ext_power);
static DEVICE_ATTR_RW(resolution); static DEVICE_ATTR_RW(resolution);
static DEVICE_ATTR_WO(eeprom);
/* Interface Functions declaration */ /* Interface Functions declaration */
...@@ -234,12 +261,14 @@ static struct attribute *w1_therm_attrs[] = { ...@@ -234,12 +261,14 @@ static struct attribute *w1_therm_attrs[] = {
&dev_attr_w1_slave.attr, &dev_attr_w1_slave.attr,
&dev_attr_ext_power.attr, &dev_attr_ext_power.attr,
&dev_attr_resolution.attr, &dev_attr_resolution.attr,
&dev_attr_eeprom.attr,
NULL, NULL,
}; };
static struct attribute *w1_ds18s20_attrs[] = { static struct attribute *w1_ds18s20_attrs[] = {
&dev_attr_w1_slave.attr, &dev_attr_w1_slave.attr,
&dev_attr_ext_power.attr, &dev_attr_ext_power.attr,
&dev_attr_eeprom.attr,
NULL, NULL,
}; };
...@@ -248,6 +277,7 @@ static struct attribute *w1_ds28ea00_attrs[] = { ...@@ -248,6 +277,7 @@ static struct attribute *w1_ds28ea00_attrs[] = {
&dev_attr_w1_seq.attr, &dev_attr_w1_seq.attr,
&dev_attr_ext_power.attr, &dev_attr_ext_power.attr,
&dev_attr_resolution.attr, &dev_attr_resolution.attr,
&dev_attr_eeprom.attr,
NULL, NULL,
}; };
...@@ -359,9 +389,6 @@ static struct w1_family w1_therm_family_DS1825 = { ...@@ -359,9 +389,6 @@ static struct w1_family w1_therm_family_DS1825 = {
/* Device dependent func */ /* 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, static inline int w1_DS18B20_write_data(struct w1_slave *sl,
const u8 *data) const u8 *data)
{ {
...@@ -477,35 +504,30 @@ static struct w1_therm_family_converter w1_therm_families[] = { ...@@ -477,35 +504,30 @@ static struct w1_therm_family_converter w1_therm_families[] = {
.convert = w1_DS18S20_convert_temp, .convert = w1_DS18S20_convert_temp,
.set_resolution = NULL, /* no config register */ .set_resolution = NULL, /* no config register */
.get_resolution = NULL, /* no config register */ .get_resolution = NULL, /* no config register */
.eeprom = w1_therm_eeprom
}, },
{ {
.f = &w1_therm_family_DS1822, .f = &w1_therm_family_DS1822,
.convert = w1_DS18B20_convert_temp, .convert = w1_DS18B20_convert_temp,
.set_resolution = w1_DS18B20_set_resolution, .set_resolution = w1_DS18B20_set_resolution,
.get_resolution = w1_DS18B20_get_resolution, .get_resolution = w1_DS18B20_get_resolution,
.eeprom = w1_therm_eeprom
}, },
{ {
.f = &w1_therm_family_DS18B20, .f = &w1_therm_family_DS18B20,
.convert = w1_DS18B20_convert_temp, .convert = w1_DS18B20_convert_temp,
.set_resolution = w1_DS18B20_set_resolution, .set_resolution = w1_DS18B20_set_resolution,
.get_resolution = w1_DS18B20_get_resolution, .get_resolution = w1_DS18B20_get_resolution,
.eeprom = w1_therm_eeprom
}, },
{ {
.f = &w1_therm_family_DS28EA00, .f = &w1_therm_family_DS28EA00,
.convert = w1_DS18B20_convert_temp, .convert = w1_DS18B20_convert_temp,
.set_resolution = w1_DS18B20_set_resolution, .set_resolution = w1_DS18B20_set_resolution,
.get_resolution = w1_DS18B20_get_resolution, .get_resolution = w1_DS18B20_get_resolution,
.eeprom = w1_therm_eeprom
}, },
{ {
.f = &w1_therm_family_DS1825, .f = &w1_therm_family_DS1825,
.convert = w1_DS18B20_convert_temp, .convert = w1_DS18B20_convert_temp,
.set_resolution = w1_DS18B20_set_resolution, .set_resolution = w1_DS18B20_set_resolution,
.get_resolution = w1_DS18B20_get_resolution, .get_resolution = w1_DS18B20_get_resolution,
.eeprom = w1_therm_eeprom
} }
}; };
...@@ -838,75 +860,94 @@ static int write_scratchpad(struct w1_slave *sl, const u8 *data, u8 nb_bytes) ...@@ -838,75 +860,94 @@ static int write_scratchpad(struct w1_slave *sl, const u8 *data, u8 nb_bytes)
return ret; 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_master = sl->master;
struct w1_master *dev = sl->master; int max_trying = W1_THERM_MAX_TRY;
u8 rom[9], external_power; int t_write, ret = -ENODEV;
int ret, max_trying = 10; bool strong_pullup;
u8 *family_data = sl->family_data;
if (!sl->family_data) { if (!sl->family_data)
ret = -ENODEV;
goto error; 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 */ /* 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 (!bus_mutex_lock(&dev_master->bus_mutex)) {
if (ret != 0) ret = -EAGAIN; /* Didn't acquire the mutex */
goto dec_refcnt; goto dec_refcnt;
}
memset(rom, 0, sizeof(rom)); while (max_trying-- && ret) { /* ret should be 0 */
/* safe version to select slave */
while (max_trying--) {
if (!reset_select_slave(sl)) { if (!reset_select_slave(sl)) {
unsigned int tm = 10;
unsigned long sleep_rem; unsigned long sleep_rem;
/* check if in parasite mode */ /* 10ms strong pullup (or delay) after the convert */
w1_write_8(dev, W1_READ_PSUPPLY); if (strong_pullup)
external_power = w1_read_8(dev); w1_next_pullup(dev_master, t_write);
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);
w1_write_8(dev, W1_COPY_SCRATCHPAD); w1_write_8(dev_master, W1_COPY_SCRATCHPAD);
if (external_power) {
mutex_unlock(&dev->bus_mutex);
sleep_rem = msleep_interruptible(tm); if (strong_pullup) {
if (sleep_rem != 0) { sleep_rem = msleep_interruptible(t_write);
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 (sleep_rem != 0) { if (sleep_rem != 0) {
ret = -EINTR; ret = -EINTR;
goto mt_unlock; goto mt_unlock;
} }
} }
ret = 0;
break;
} }
} }
mt_unlock: mt_unlock:
mutex_unlock(&dev->bus_mutex); mutex_unlock(&dev_master->bus_mutex);
dec_refcnt: 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: error:
return ret; return ret;
} }
...@@ -1006,7 +1047,7 @@ static ssize_t w1_slave_store(struct device *device, ...@@ -1006,7 +1047,7 @@ static ssize_t w1_slave_store(struct device *device,
} }
if (val == 0) /* val=0 : trigger a EEPROM save */ if (val == 0) /* val=0 : trigger a EEPROM save */
ret = SLAVE_SPECIFIC_FUNC(sl)->eeprom(device); ret = copy_scratchpad(sl);
else { else {
if (SLAVE_SPECIFIC_FUNC(sl)->set_resolution) if (SLAVE_SPECIFIC_FUNC(sl)->set_resolution)
ret = SLAVE_SPECIFIC_FUNC(sl)->set_resolution(sl, val); ret = SLAVE_SPECIFIC_FUNC(sl)->set_resolution(sl, val);
...@@ -1105,6 +1146,26 @@ static ssize_t resolution_store(struct device *device, ...@@ -1105,6 +1146,26 @@ static ssize_t resolution_store(struct device *device,
return size; 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) #if IS_REACHABLE(CONFIG_HWMON)
static int w1_read_temp(struct device *device, u32 attr, int channel, static int w1_read_temp(struct device *device, u32 attr, int channel,
long *val) long *val)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment