Commit c8ac32e4 authored by Manuel Lauss's avatar Manuel Lauss Committed by Jean Delvare

hwmon: (lm70) Add TI TMP121 support

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.  The TMP123 differs in pin assign-
ment.
Signed-off-by: default avatarManuel Lauss <mano@roarinelk.homelinux.net>
Signed-off-by: default avatarJean Delvare <khali@linux-fr.org>
parent 2b730051
Kernel driver lm70 Kernel driver lm70
================== ==================
Supported chip: Supported chips:
* National Semiconductor LM70 * National Semiconductor LM70
Datasheet: http://www.national.com/pf/LM/LM70.html Datasheet: http://www.national.com/pf/LM/LM70.html
* Texas Instruments TMP121/TMP123
Information: http://focus.ti.com/docs/prod/folders/print/tmp121.html
Author: Author:
Kaiwan N Billimoria <kaiwan@designergraphix.com> Kaiwan N Billimoria <kaiwan@designergraphix.com>
...@@ -29,6 +31,10 @@ As a real (in-tree) example of this "SPI protocol driver" interfacing ...@@ -29,6 +31,10 @@ As a real (in-tree) example of this "SPI protocol driver" interfacing
with a "SPI master controller driver", see drivers/spi/spi_lm70llp.c with a "SPI master controller driver", see drivers/spi/spi_lm70llp.c
and its associated documentation. and its associated documentation.
The TMP121/TMP123 are very similar; main differences are 4 wire SPI inter-
face (read only) and 13-bit temperature data (0.0625 degrees celsius reso-
lution).
Thanks to Thanks to
--------- ---------
Jean Delvare <khali@linux-fr.org> for mentoring the hwmon-side driver Jean Delvare <khali@linux-fr.org> for mentoring the hwmon-side driver
......
...@@ -428,11 +428,12 @@ config SENSORS_LM63 ...@@ -428,11 +428,12 @@ config SENSORS_LM63
will be called lm63. will be called lm63.
config SENSORS_LM70 config SENSORS_LM70
tristate "National Semiconductor LM70" tristate "National Semiconductor LM70 / Texas Instruments TMP121"
depends on SPI_MASTER && EXPERIMENTAL depends on SPI_MASTER && EXPERIMENTAL
help help
If you say yes here you get support for the National Semiconductor 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 This driver can also be built as a module. If so, the module
will be called lm70. will be called lm70.
......
...@@ -37,9 +37,13 @@ ...@@ -37,9 +37,13 @@
#define DRVNAME "lm70" #define DRVNAME "lm70"
#define LM70_CHIP_LM70 0 /* original NS LM70 */
#define LM70_CHIP_TMP121 1 /* TI TMP121/TMP123 */
struct lm70 { struct lm70 {
struct device *hwmon_dev; struct device *hwmon_dev;
struct mutex lock; struct mutex lock;
unsigned int chip;
}; };
/* sysfs hook function */ /* sysfs hook function */
...@@ -47,7 +51,7 @@ static ssize_t lm70_sense_temp(struct device *dev, ...@@ -47,7 +51,7 @@ static ssize_t lm70_sense_temp(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
struct spi_device *spi = to_spi_device(dev); struct spi_device *spi = to_spi_device(dev);
int status, val; int status, val = 0;
u8 rxbuf[2]; u8 rxbuf[2];
s16 raw=0; s16 raw=0;
struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev); struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev);
...@@ -70,6 +74,7 @@ static ssize_t lm70_sense_temp(struct device *dev, ...@@ -70,6 +74,7 @@ static ssize_t lm70_sense_temp(struct device *dev,
rxbuf[0], rxbuf[1], raw); rxbuf[0], rxbuf[1], raw);
/* /*
* LM70:
* The "raw" temperature read into rxbuf[] is a 16-bit signed 2's * The "raw" temperature read into rxbuf[] is a 16-bit signed 2's
* complement value. Only the MSB 11 bits (1 sign + 10 temperature * complement value. Only the MSB 11 bits (1 sign + 10 temperature
* bits) are meaningful; the LSB 5 bits are to be discarded. * bits) are meaningful; the LSB 5 bits are to be discarded.
...@@ -79,8 +84,21 @@ static ssize_t lm70_sense_temp(struct device *dev, ...@@ -79,8 +84,21 @@ static ssize_t lm70_sense_temp(struct device *dev,
* by 0.25. Also multiply by 1000 to represent in millidegrees * by 0.25. Also multiply by 1000 to represent in millidegrees
* Celsius. * Celsius.
* So it's equivalent to multiplying by 0.25 * 1000 = 250. * So it's equivalent to multiplying by 0.25 * 1000 = 250.
*
* TMP121/TMP123:
* 13 bits of 2's complement data, discard LSB 3 bits,
* resolution 0.0625 degrees celsius.
*/ */
val = ((int)raw/32) * 250; switch (p_lm70->chip) {
case LM70_CHIP_LM70:
val = ((int)raw / 32) * 250;
break;
case LM70_CHIP_TMP121:
val = ((int)raw / 8) * 625 / 10;
break;
}
status = sprintf(buf, "%d\n", val); /* millidegrees Celsius */ status = sprintf(buf, "%d\n", val); /* millidegrees Celsius */
out: out:
mutex_unlock(&p_lm70->lock); mutex_unlock(&p_lm70->lock);
...@@ -92,22 +110,31 @@ static DEVICE_ATTR(temp1_input, S_IRUGO, lm70_sense_temp, NULL); ...@@ -92,22 +110,31 @@ static DEVICE_ATTR(temp1_input, S_IRUGO, lm70_sense_temp, NULL);
static ssize_t lm70_show_name(struct device *dev, struct device_attribute static ssize_t lm70_show_name(struct device *dev, struct device_attribute
*devattr, char *buf) *devattr, char *buf)
{ {
return sprintf(buf, "lm70\n"); struct lm70 *p_lm70 = dev_get_drvdata(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); static DEVICE_ATTR(name, S_IRUGO, lm70_show_name, NULL);
/*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/
static int __devinit lm70_probe(struct spi_device *spi) static int __devinit common_probe(struct spi_device *spi, int chip)
{ {
struct lm70 *p_lm70; struct lm70 *p_lm70;
int status; int status;
/* signaling is SPI_MODE_0 on a 3-wire link (shared SI/SO) */
if ((spi->mode & (SPI_CPOL|SPI_CPHA)) || !(spi->mode & SPI_3WIRE))
return -EINVAL;
/* NOTE: we assume 8-bit words, and convert to 16 bits manually */ /* NOTE: we assume 8-bit words, and convert to 16 bits manually */
p_lm70 = kzalloc(sizeof *p_lm70, GFP_KERNEL); p_lm70 = kzalloc(sizeof *p_lm70, GFP_KERNEL);
...@@ -115,6 +142,7 @@ static int __devinit lm70_probe(struct spi_device *spi) ...@@ -115,6 +142,7 @@ static int __devinit lm70_probe(struct spi_device *spi)
return -ENOMEM; return -ENOMEM;
mutex_init(&p_lm70->lock); mutex_init(&p_lm70->lock);
p_lm70->chip = chip;
/* sysfs hook */ /* sysfs hook */
p_lm70->hwmon_dev = hwmon_device_register(&spi->dev); p_lm70->hwmon_dev = hwmon_device_register(&spi->dev);
...@@ -142,6 +170,24 @@ out_dev_reg_failed: ...@@ -142,6 +170,24 @@ out_dev_reg_failed:
return status; return status;
} }
static int __devinit lm70_probe(struct spi_device *spi)
{
/* signaling is SPI_MODE_0 on a 3-wire link (shared SI/SO) */
if ((spi->mode & (SPI_CPOL | SPI_CPHA)) || !(spi->mode & SPI_3WIRE))
return -EINVAL;
return common_probe(spi, LM70_CHIP_LM70);
}
static int __devinit tmp121_probe(struct spi_device *spi)
{
/* signaling is SPI_MODE_0 with only MISO connected */
if (spi->mode & (SPI_CPOL | SPI_CPHA))
return -EINVAL;
return common_probe(spi, LM70_CHIP_TMP121);
}
static int __devexit lm70_remove(struct spi_device *spi) static int __devexit lm70_remove(struct spi_device *spi)
{ {
struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev); struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev);
...@@ -155,6 +201,15 @@ static int __devexit lm70_remove(struct spi_device *spi) ...@@ -155,6 +201,15 @@ static int __devexit lm70_remove(struct spi_device *spi)
return 0; return 0;
} }
static struct spi_driver tmp121_driver = {
.driver = {
.name = "tmp121",
.owner = THIS_MODULE,
},
.probe = tmp121_probe,
.remove = __devexit_p(lm70_remove),
};
static struct spi_driver lm70_driver = { static struct spi_driver lm70_driver = {
.driver = { .driver = {
.name = "lm70", .name = "lm70",
...@@ -166,17 +221,26 @@ static struct spi_driver lm70_driver = { ...@@ -166,17 +221,26 @@ static struct spi_driver lm70_driver = {
static int __init init_lm70(void) static int __init init_lm70(void)
{ {
return spi_register_driver(&lm70_driver); int ret = spi_register_driver(&lm70_driver);
if (ret)
return ret;
ret = spi_register_driver(&tmp121_driver);
if (ret)
spi_unregister_driver(&lm70_driver);
return ret;
} }
static void __exit cleanup_lm70(void) static void __exit cleanup_lm70(void)
{ {
spi_unregister_driver(&lm70_driver); spi_unregister_driver(&lm70_driver);
spi_unregister_driver(&tmp121_driver);
} }
module_init(init_lm70); module_init(init_lm70);
module_exit(cleanup_lm70); module_exit(cleanup_lm70);
MODULE_AUTHOR("Kaiwan N Billimoria"); MODULE_AUTHOR("Kaiwan N Billimoria");
MODULE_DESCRIPTION("National Semiconductor LM70 Linux driver"); MODULE_DESCRIPTION("NS LM70 / TI TMP121/TMP123 Linux driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
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