The Texas Instruments TMP121 is a SPI temperature sensor very similar to the LM70, with slightly higher resolution. This patch extends the LM70 driver to support the TMP121. Chip type is determined by platform data. Signed-off-by: Manuel Lauss <mano at roarinelk.homelinux.net> --- Documentation/hwmon/lm70 | 7 +++++- drivers/hwmon/Kconfig | 5 ++- drivers/hwmon/lm70.c | 56 ++++++++++++++++++++++++++++++++++++++++----- include/linux/spi/lm70.h | 15 ++++++++++++ 4 files changed, 73 insertions(+), 10 deletions(-) create mode 100644 include/linux/spi/lm70.h diff --git a/Documentation/hwmon/lm70 b/Documentation/hwmon/lm70 index 2bdd3fe..88f4322 100644 --- a/Documentation/hwmon/lm70 +++ b/Documentation/hwmon/lm70 @@ -1,9 +1,11 @@ Kernel driver lm70 ================== -Supported chip: +Supported chips: * National Semiconductor LM70 Datasheet: http://www.national.com/pf/LM/LM70.html + * Texas Instruments TMP121/TMP123 + Datasheet: http://www.ti.com/lit/gpn/tmp121 Author: Kaiwan N Billimoria <kaiwan at designergraphix.com> @@ -25,6 +27,9 @@ complement digital temperature (sent via the SIO line), is available in the driver for interpretation. This driver makes use of the kernel's in-core SPI support. +The TMP121 is very similar; main differences are 4 wire SPI interface, +and 13bit temperature data (0.0625 celsius resolution). + Thanks to --------- Jean Delvare <khali at linux-fr.org> for mentoring the hwmon-side driver diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index d402e8d..56a84e0 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -407,11 +407,12 @@ config SENSORS_LM63 will be called lm63. config SENSORS_LM70 - tristate "National Semiconductor LM70" + tristate "National Semiconductor LM70 / Texas Instruments TMP121" depends on SPI_MASTER && EXPERIMENTAL help If you say yes here you get support for the National Semiconductor - LM70 digital temperature sensor chip. + LM70 and Texas Instruments TMP121/TMP123 digital temperature + sensor chips. This driver can also be built as a module. If so, the module will be called lm70. diff --git a/drivers/hwmon/lm70.c b/drivers/hwmon/lm70.c index d435f00..9ab0479 100644 --- a/drivers/hwmon/lm70.c +++ b/drivers/hwmon/lm70.c @@ -33,13 +33,14 @@ #include <linux/hwmon.h> #include <linux/mutex.h> #include <linux/spi/spi.h> - +#include <linux/spi/lm70.h> #define DRVNAME "lm70" struct lm70 { struct device *hwmon_dev; struct mutex lock; + unsigned int chip; }; /* sysfs hook function */ @@ -67,10 +68,8 @@ static ssize_t lm70_sense_temp(struct device *dev, } dev_dbg(dev, "rxbuf[1] : 0x%x rxbuf[0] : 0x%x\n", rxbuf[1], rxbuf[0]); - raw = (rxbuf[1] << 8) + rxbuf[0]; - dev_dbg(dev, "raw=0x%x\n", raw); - /* + * LM70: * The "raw" temperature read into rxbuf[] is a 16-bit signed 2's * complement value. Only the MSB 11 bits (1 sign + 10 temperature * bits) are meaningful; the LSB 5 bits are to be discarded. @@ -80,8 +79,27 @@ static ssize_t lm70_sense_temp(struct device *dev, * by 0.25. Also multiply by 1000 to represent in millidegrees * Celsius. * So it's equivalent to multiplying by 0.25 * 1000 = 250. + * + * TMP121: + * 13bits of 2's complement data, discard lower 3 bits. Chip + * transmits high byte first. Resolution 0.0625 celsius. */ - val = ((int)raw/32) * 250; + switch (p_lm70->chip) { + case LM70_CHIP_LM70: + raw = (rxbuf[1] << 8) + rxbuf[0]; + dev_dbg(dev, "raw=0x%x\n", raw); + val = ((int)raw/32) * 250; + break; + + case LM70_CHIP_TMP121: + raw = (rxbuf[0] << 8) + rxbuf[1]; + dev_dbg(dev, "raw=0x%x\n", raw); + val = (raw / 8) * 625 / 10; + break; + + default: + raw = 0; + } status = sprintf(buf, "%d\n", val); /* millidegrees Celsius */ out: mutex_unlock(&p_lm70->lock); @@ -93,7 +111,21 @@ static DEVICE_ATTR(temp1_input, S_IRUGO, lm70_sense_temp, NULL); static ssize_t lm70_show_name(struct device *dev, struct device_attribute *devattr, char *buf) { - return sprintf(buf, "lm70\n"); + struct spi_device *spi = to_spi_device(dev); + struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev); + int ret; + + switch (p_lm70->chip) { + case LM70_CHIP_LM70: + ret = sprintf(buf, "lm70\n"); + break; + case LM70_CHIP_TMP121: + ret = sprintf(buf, "tmp121\n"); + break; + default: + ret = -EINVAL; + } + return ret; } static DEVICE_ATTR(name, S_IRUGO, lm70_show_name, NULL); @@ -102,11 +134,20 @@ static DEVICE_ATTR(name, S_IRUGO, lm70_show_name, NULL); static int __devinit lm70_probe(struct spi_device *spi) { + struct lm70_platform_data *pdata; struct lm70 *p_lm70; int status; + unsigned int chip; + + pdata = spi->dev.platform_data; + if (pdata) + chip = pdata->chip; + else + chip = LM70_CHIP_LM70; /* signaling is SPI_MODE_0 on a 3-wire link (shared SI/SO) */ - if ((spi->mode & (SPI_CPOL|SPI_CPHA)) || !(spi->mode & SPI_3WIRE)) + if ((chip == LM70_CHIP_LM70) && + ((spi->mode & (SPI_CPOL|SPI_CPHA)) || !(spi->mode & SPI_3WIRE))) return -EINVAL; p_lm70 = kzalloc(sizeof *p_lm70, GFP_KERNEL); @@ -114,6 +155,7 @@ static int __devinit lm70_probe(struct spi_device *spi) return -ENOMEM; mutex_init(&p_lm70->lock); + p_lm70->chip = chip; /* sysfs hook */ p_lm70->hwmon_dev = hwmon_device_register(&spi->dev); diff --git a/include/linux/spi/lm70.h b/include/linux/spi/lm70.h new file mode 100644 index 0000000..986c35a --- /dev/null +++ b/include/linux/spi/lm70.h @@ -0,0 +1,15 @@ +/* + * LM70 platform data + */ + +#ifndef _LM70_H_ +#define _LM70_H_ + +#define LM70_CHIP_LM70 0 +#define LM70_CHIP_TMP121 1 + +struct lm70_platform_data { + unsigned int chip; +}; + +#endif -- 1.6.0.1