summaryrefslogtreecommitdiff
path: root/drivers/w1/slaves/w1_therm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/w1/slaves/w1_therm.c')
-rw-r--r--drivers/w1/slaves/w1_therm.c251
1 files changed, 249 insertions, 2 deletions
diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c
index 825536196e61..cc4b88056b33 100644
--- 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_strong_pullup, int, 0);
#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_strong_pullup, int, 0);
#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_strong_pullup, int, 0);
* @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 *sl);
*/
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 device *device,
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 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,
@@ -580,6 +618,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,
@@ -588,6 +627,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,
@@ -596,6 +636,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,
@@ -604,6 +645,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
}
};
@@ -658,6 +700,23 @@ static inline bool bus_mutex_lock(struct mutex *lock)
}
/**
+ * 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_slave *sl)
/* 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_slave *sl)
}
}
+ /* Finally initialize convert_triggered flag */
+ SLAVE_CONVERT_TRIGGERED(sl) = 0;
+
return 0;
}
@@ -770,6 +850,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));
@@ -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 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,
@@ -1176,7 +1367,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,
@@ -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)