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.c286
1 files changed, 185 insertions, 101 deletions
diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c
index 1ba07b0833fa..5a81ce2a4e74 100644
--- a/drivers/w1/slaves/w1_therm.c
+++ b/drivers/w1/slaves/w1_therm.c
@@ -93,6 +93,7 @@ module_param_named(strong_pullup, w1_strong_pullup, int, 0);
* @reserved: not used here
* @f: pointer to the device binding structure
* @convert: pointer to the device conversion function
+ * @get_conversion_time: pointer to the device conversion time function
* @set_resolution: pointer to the device set_resolution function
* @get_resolution: pointer to the device get_resolution function
*/
@@ -101,6 +102,7 @@ struct w1_therm_family_converter {
u16 reserved;
struct w1_family *f;
int (*convert)(u8 rom[9]);
+ int (*get_conversion_time)(struct w1_slave *sl);
int (*set_resolution)(struct w1_slave *sl, int val);
int (*get_resolution)(struct w1_slave *sl);
};
@@ -154,6 +156,15 @@ struct therm_info {
static int reset_select_slave(struct w1_slave *sl);
/**
+ * convert_t() - Query the device for temperature conversion and read
+ * @sl: pointer to the slave to read
+ * @info: pointer to a structure to store the read results
+ *
+ * Return: 0 if success, -kernel error code otherwise
+ */
+static int convert_t(struct w1_slave *sl, struct therm_info *info);
+
+/**
* read_scratchpad() - read the data in device RAM
* @sl: pointer to the slave to read
* @info: pointer to a structure to store the read results
@@ -213,6 +224,9 @@ static ssize_t w1_slave_store(struct device *device,
static ssize_t w1_seq_show(struct device *device,
struct device_attribute *attr, char *buf);
+static ssize_t temperature_show(struct device *device,
+ struct device_attribute *attr, char *buf);
+
static ssize_t ext_power_show(struct device *device,
struct device_attribute *attr, char *buf);
@@ -229,6 +243,7 @@ static ssize_t eeprom_store(struct device *device,
static DEVICE_ATTR_RW(w1_slave);
static DEVICE_ATTR_RO(w1_seq);
+static DEVICE_ATTR_RO(temperature);
static DEVICE_ATTR_RO(ext_power);
static DEVICE_ATTR_RW(resolution);
static DEVICE_ATTR_WO(eeprom);
@@ -259,6 +274,7 @@ static void w1_therm_remove_slave(struct w1_slave *sl);
static struct attribute *w1_therm_attrs[] = {
&dev_attr_w1_slave.attr,
+ &dev_attr_temperature.attr,
&dev_attr_ext_power.attr,
&dev_attr_resolution.attr,
&dev_attr_eeprom.attr,
@@ -267,6 +283,7 @@ static struct attribute *w1_therm_attrs[] = {
static struct attribute *w1_ds18s20_attrs[] = {
&dev_attr_w1_slave.attr,
+ &dev_attr_temperature.attr,
&dev_attr_ext_power.attr,
&dev_attr_eeprom.attr,
NULL,
@@ -275,6 +292,7 @@ static struct attribute *w1_ds18s20_attrs[] = {
static struct attribute *w1_ds28ea00_attrs[] = {
&dev_attr_w1_slave.attr,
&dev_attr_w1_seq.attr,
+ &dev_attr_temperature.attr,
&dev_attr_ext_power.attr,
&dev_attr_resolution.attr,
&dev_attr_eeprom.attr,
@@ -389,6 +407,37 @@ static struct w1_family w1_therm_family_DS1825 = {
/* Device dependent func */
+static inline int w1_DS18B20_convert_time(struct w1_slave *sl)
+{
+ int ret;
+
+ if (!sl->family_data)
+ return -ENODEV; /* device unknown */
+
+ /* return time in ms for conversion operation */
+ switch (SLAVE_RESOLUTION(sl)) {
+ case 9:
+ ret = 95;
+ break;
+ case 10:
+ ret = 190;
+ break;
+ case 11:
+ ret = 375;
+ break;
+ case 12:
+ default:
+ ret = 750;
+ }
+ return ret;
+}
+
+static inline int w1_DS18S20_convert_time(struct w1_slave *sl)
+{
+ (void)(sl);
+ return 750; /* always 750ms for DS18S20 */
+}
+
static inline int w1_DS18B20_write_data(struct w1_slave *sl,
const u8 *data)
{
@@ -480,8 +529,10 @@ static inline int w1_DS18S20_convert_temp(u8 rom[9])
{
int t, h;
- if (!rom[7])
+ if (!rom[7]) {
+ pr_debug("%s: Invalid argument for conversion\n", __func__);
return 0;
+ }
if (rom[1] == 0)
t = ((s32)rom[0] >> 1)*1000;
@@ -500,34 +551,39 @@ static inline int w1_DS18S20_convert_temp(u8 rom[9])
static struct w1_therm_family_converter w1_therm_families[] = {
{
- .f = &w1_therm_family_DS18S20,
- .convert = w1_DS18S20_convert_temp,
- .set_resolution = NULL, /* no config register */
- .get_resolution = NULL, /* no config register */
+ .f = &w1_therm_family_DS18S20,
+ .convert = w1_DS18S20_convert_temp,
+ .get_conversion_time = w1_DS18S20_convert_time,
+ .set_resolution = NULL, /* no config register */
+ .get_resolution = NULL, /* no config register */
},
{
- .f = &w1_therm_family_DS1822,
- .convert = w1_DS18B20_convert_temp,
- .set_resolution = w1_DS18B20_set_resolution,
- .get_resolution = w1_DS18B20_get_resolution,
+ .f = &w1_therm_family_DS1822,
+ .convert = w1_DS18B20_convert_temp,
+ .get_conversion_time = w1_DS18B20_convert_time,
+ .set_resolution = w1_DS18B20_set_resolution,
+ .get_resolution = w1_DS18B20_get_resolution,
},
{
- .f = &w1_therm_family_DS18B20,
- .convert = w1_DS18B20_convert_temp,
- .set_resolution = w1_DS18B20_set_resolution,
- .get_resolution = w1_DS18B20_get_resolution,
+ .f = &w1_therm_family_DS18B20,
+ .convert = w1_DS18B20_convert_temp,
+ .get_conversion_time = w1_DS18B20_convert_time,
+ .set_resolution = w1_DS18B20_set_resolution,
+ .get_resolution = w1_DS18B20_get_resolution,
},
{
- .f = &w1_therm_family_DS28EA00,
- .convert = w1_DS18B20_convert_temp,
- .set_resolution = w1_DS18B20_set_resolution,
- .get_resolution = w1_DS18B20_get_resolution,
+ .f = &w1_therm_family_DS28EA00,
+ .convert = w1_DS18B20_convert_temp,
+ .get_conversion_time = w1_DS18B20_convert_time,
+ .set_resolution = w1_DS18B20_set_resolution,
+ .get_resolution = w1_DS18B20_get_resolution,
},
{
- .f = &w1_therm_family_DS1825,
- .convert = w1_DS18B20_convert_temp,
- .set_resolution = w1_DS18B20_set_resolution,
- .get_resolution = w1_DS18B20_get_resolution,
+ .f = &w1_therm_family_DS1825,
+ .convert = w1_DS18B20_convert_temp,
+ .get_conversion_time = w1_DS18B20_convert_time,
+ .set_resolution = w1_DS18B20_set_resolution,
+ .get_resolution = w1_DS18B20_get_resolution,
}
};
@@ -582,24 +638,44 @@ static inline bool bus_mutex_lock(struct mutex *lock)
}
/**
- * w1_convert_temp() - temperature conversion binding function
- * @rom: data read from device RAM (8 data bytes + 1 CRC byte)
- * @fid: device family id
+ * conversion_time() - get the Tconv for the slave
+ * @sl: device to get the conversion time
*
- * The function call the temperature computation function according to
- * device family.
+ * On device supporting resolution settings, conversion time depend
+ * on the resolution setting. This helper function get the slave timing,
+ * depending on its current setting.
*
- * Return: value in millidegrees Celsius.
+ * Return: conversion time in ms, negative values are kernel error code
*/
-static inline int w1_convert_temp(u8 rom[9], u8 fid)
+static inline int conversion_time(struct w1_slave *sl)
{
- int i;
+ if (SLAVE_SPECIFIC_FUNC(sl))
+ return SLAVE_SPECIFIC_FUNC(sl)->get_conversion_time(sl);
- for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i)
- if (w1_therm_families[i].f->fid == fid)
- return w1_therm_families[i].convert(rom);
+ dev_info(&sl->dev,
+ "%s: Device not supported by the driver\n", __func__);
- return 0;
+ return -ENODEV; /* No device family */
+}
+
+/**
+ * temperature_from_RAM() - Convert the read info to temperature
+ * @sl: device that sent the RAM data
+ * @rom: read value on the slave device RAM
+ *
+ * Device dependent, the function bind the correct computation method.
+ *
+ * Return: temperature in 1/1000degC, 0 on error.
+ */
+static inline int temperature_from_RAM(struct w1_slave *sl, u8 rom[9])
+{
+ if (SLAVE_SPECIFIC_FUNC(sl))
+ return SLAVE_SPECIFIC_FUNC(sl)->convert(rom);
+
+ dev_info(&sl->dev,
+ "%s: Device not supported by the driver\n", __func__);
+
+ return 0; /* No device family */
}
/* Interface Functions */
@@ -679,96 +755,74 @@ static int reset_select_slave(struct w1_slave *sl)
return 0;
}
-static ssize_t read_therm(struct device *device,
- struct w1_slave *sl, struct therm_info *info)
+static int convert_t(struct w1_slave *sl, struct therm_info *info)
{
- struct w1_master *dev = sl->master;
- u8 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_conv;
+ int ret = -ENODEV;
+ bool strong_pullup;
- if (!family_data) {
- ret = -ENODEV;
+ if (!sl->family_data)
goto error;
- }
- /* prevent the slave from going away in sleep */
- atomic_inc(THERM_REFCNT(family_data));
+ strong_pullup = (w1_strong_pullup == 2 ||
+ (!SLAVE_POWERMODE(sl) &&
+ w1_strong_pullup));
- ret = mutex_lock_interruptible(&dev->bus_mutex);
- if (ret != 0)
- goto dec_refcnt;
+ /* get conversion duration device and id dependent */
+ t_conv = conversion_time(sl);
memset(info->rom, 0, sizeof(info->rom));
- while (max_trying--) {
+ /* 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 */
info->verdict = 0;
info->crc = 0;
-
+ /* safe version to select slave */
if (!reset_select_slave(sl)) {
- int count = 0;
- unsigned int tm = 750;
unsigned long sleep_rem;
- w1_write_8(dev, W1_READ_PSUPPLY);
- external_power = w1_read_8(dev);
-
- if (reset_select_slave(sl))
- continue;
-
/* 750ms strong pullup (or delay) after the convert */
- if (w1_strong_pullup == 2 ||
- (!external_power && w1_strong_pullup))
- w1_next_pullup(dev, tm);
-
- w1_write_8(dev, W1_CONVERT_TEMP);
+ if (strong_pullup)
+ w1_next_pullup(dev_master, t_conv);
- if (external_power) {
- mutex_unlock(&dev->bus_mutex);
+ w1_write_8(dev_master, W1_CONVERT_TEMP);
- sleep_rem = msleep_interruptible(tm);
+ if (strong_pullup) { /*some device need pullup */
+ sleep_rem = msleep_interruptible(t_conv);
if (sleep_rem != 0) {
ret = -EINTR;
- goto dec_refcnt;
+ goto mt_unlock;
}
+ mutex_unlock(&dev_master->bus_mutex);
+ } else { /*no device need pullup */
+ mutex_unlock(&dev_master->bus_mutex);
- ret = mutex_lock_interruptible(&dev->bus_mutex);
- if (ret != 0)
- goto dec_refcnt;
- } else if (!w1_strong_pullup) {
- sleep_rem = msleep_interruptible(tm);
+ sleep_rem = msleep_interruptible(t_conv);
if (sleep_rem != 0) {
ret = -EINTR;
- goto mt_unlock;
- }
- }
-
- if (!reset_select_slave(sl)) {
-
- w1_write_8(dev, W1_READ_SCRATCHPAD);
- count = w1_read_block(dev, info->rom, 9);
- if (count != 9) {
- dev_warn(device, "w1_read_block() "
- "returned %u instead of 9.\n",
- count);
+ goto dec_refcnt;
}
-
- info->crc = w1_calc_crc8(info->rom, 8);
-
- if (info->rom[8] == info->crc)
- info->verdict = 1;
}
+ ret = read_scratchpad(sl, info);
+ goto dec_refcnt;
}
- if (info->verdict)
- break;
}
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;
}
@@ -1000,27 +1054,33 @@ static ssize_t w1_slave_show(struct device *device,
u8 *family_data = sl->family_data;
int ret, i;
ssize_t c = PAGE_SIZE;
- u8 fid = sl->family->fid;
- ret = read_therm(device, sl, &info);
- if (ret)
- return ret;
+ ret = convert_t(sl, &info);
+
+ if (ret < 0) {
+ dev_dbg(device,
+ "%s: Temperature data may be corrupted. err=%d\n",
+ __func__, ret);
+ return 0;
+ }
for (i = 0; i < 9; ++i)
c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", info.rom[i]);
c -= snprintf(buf + PAGE_SIZE - c, c, ": crc=%02x %s\n",
info.crc, (info.verdict) ? "YES" : "NO");
+
if (info.verdict)
memcpy(family_data, info.rom, sizeof(info.rom));
else
- dev_warn(device, "Read failed CRC check\n");
+ dev_warn(device, "%s:Read failed CRC check\n", __func__);
for (i = 0; i < 9; ++i)
c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ",
((u8 *)family_data)[i]);
c -= snprintf(buf + PAGE_SIZE - c, c, "t=%d\n",
- w1_convert_temp(info.rom, fid));
+ temperature_from_RAM(sl, info.rom));
+
ret = PAGE_SIZE - c;
return ret;
}
@@ -1063,6 +1123,31 @@ static ssize_t w1_slave_store(struct device *device,
return size; /* always return size to avoid infinite calling */
}
+static ssize_t temperature_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct w1_slave *sl = dev_to_w1_slave(device);
+ struct therm_info info;
+ int ret = 0;
+
+ if ((!sl->family_data) || (!SLAVE_SPECIFIC_FUNC(sl))) {
+ dev_info(device,
+ "%s: Device not supported by the driver\n", __func__);
+ return 0; /* No device family */
+ }
+
+ ret = convert_t(sl, &info);
+
+ if (ret < 0) {
+ dev_dbg(device,
+ "%s: Temperature data may be corrupted. err=%d\n",
+ __func__, ret);
+ return 0;
+ }
+
+ return sprintf(buf, "%d\n", temperature_from_RAM(sl, info.rom));
+}
+
static ssize_t ext_power_show(struct device *device,
struct device_attribute *attr, char *buf)
{
@@ -1172,12 +1257,11 @@ static int w1_read_temp(struct device *device, u32 attr, int channel,
{
struct w1_slave *sl = dev_get_drvdata(device);
struct therm_info info;
- u8 fid = sl->family->fid;
int ret;
switch (attr) {
case hwmon_temp_input:
- ret = read_therm(device, sl, &info);
+ ret = convert_t(sl, &info);
if (ret)
return ret;
@@ -1186,7 +1270,7 @@ static int w1_read_temp(struct device *device, u32 attr, int channel,
return ret;
}
- *val = w1_convert_temp(info.rom, fid);
+ *val = temperature_from_RAM(sl, info.rom);
ret = 0;
break;
default: