2006-06-12 22:00:05 +02:00
/*
* lm70 . c
*
* The LM70 is a temperature sensor chip from National Semiconductor ( NS ) .
* Copyright ( C ) 2006 Kaiwan N Billimoria < kaiwan @ designergraphix . com >
*
* The LM70 communicates with a host processor via an SPI / Microwire Bus
* interface . The complete datasheet is available at National ' s website
* here :
* http : //www.national.com/pf/LM/LM70.html
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/device.h>
# include <linux/err.h>
# include <linux/sysfs.h>
# include <linux/hwmon.h>
2007-10-24 14:59:09 +02:00
# include <linux/mutex.h>
2006-06-12 22:00:05 +02:00
# include <linux/spi/spi.h>
2007-10-24 14:59:09 +02:00
2006-06-12 22:00:05 +02:00
# define DRVNAME "lm70"
struct lm70 {
2007-08-20 13:46:20 -07:00
struct device * hwmon_dev ;
2007-10-24 14:59:09 +02:00
struct mutex lock ;
2006-06-12 22:00:05 +02:00
} ;
/* sysfs hook function */
static ssize_t lm70_sense_temp ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct spi_device * spi = to_spi_device ( dev ) ;
int status , val ;
u8 rxbuf [ 2 ] ;
s16 raw = 0 ;
struct lm70 * p_lm70 = dev_get_drvdata ( & spi - > dev ) ;
2007-10-24 14:59:09 +02:00
if ( mutex_lock_interruptible ( & p_lm70 - > lock ) )
2006-06-12 22:00:05 +02:00
return - ERESTARTSYS ;
/*
* spi_read ( ) requires a DMA - safe buffer ; so we use
* spi_write_then_read ( ) , transmitting 0 bytes .
*/
status = spi_write_then_read ( spi , NULL , 0 , & rxbuf [ 0 ] , 2 ) ;
if ( status < 0 ) {
printk ( KERN_WARNING
" spi_write_then_read failed with status %d \n " , status ) ;
goto out ;
}
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 ) ;
/*
* 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 .
* See the datasheet .
*
* Further , each bit represents 0.25 degrees Celsius ; so , multiply
* by 0.25 . Also multiply by 1000 to represent in millidegrees
* Celsius .
* So it ' s equivalent to multiplying by 0.25 * 1000 = 250.
*/
val = ( ( int ) raw / 32 ) * 250 ;
2007-08-17 17:31:35 +02:00
status = sprintf ( buf , " %d \n " , val ) ; /* millidegrees Celsius */
2006-06-12 22:00:05 +02:00
out :
2007-10-24 14:59:09 +02:00
mutex_unlock ( & p_lm70 - > lock ) ;
2006-06-12 22:00:05 +02:00
return status ;
}
static DEVICE_ATTR ( temp1_input , S_IRUGO , lm70_sense_temp , NULL ) ;
2007-08-17 17:31:35 +02:00
static ssize_t lm70_show_name ( struct device * dev , struct device_attribute
* devattr , char * buf )
{
return sprintf ( buf , " lm70 \n " ) ;
}
static DEVICE_ATTR ( name , S_IRUGO , lm70_show_name , NULL ) ;
2006-06-12 22:00:05 +02:00
/*----------------------------------------------------------------------*/
static int __devinit lm70_probe ( struct spi_device * spi )
{
struct lm70 * p_lm70 ;
int status ;
2007-07-17 04:04:03 -07:00
/* 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 ;
2006-06-12 22:00:05 +02:00
p_lm70 = kzalloc ( sizeof * p_lm70 , GFP_KERNEL ) ;
if ( ! p_lm70 )
return - ENOMEM ;
2007-10-24 14:59:09 +02:00
mutex_init ( & p_lm70 - > lock ) ;
2006-06-12 22:00:05 +02:00
/* sysfs hook */
2007-08-20 13:46:20 -07:00
p_lm70 - > hwmon_dev = hwmon_device_register ( & spi - > dev ) ;
if ( IS_ERR ( p_lm70 - > hwmon_dev ) ) {
2006-06-12 22:00:05 +02:00
dev_dbg ( & spi - > dev , " hwmon_device_register failed. \n " ) ;
2007-08-20 13:46:20 -07:00
status = PTR_ERR ( p_lm70 - > hwmon_dev ) ;
2006-06-12 22:00:05 +02:00
goto out_dev_reg_failed ;
}
dev_set_drvdata ( & spi - > dev , p_lm70 ) ;
2007-08-17 17:31:35 +02:00
if ( ( status = device_create_file ( & spi - > dev , & dev_attr_temp1_input ) )
| | ( status = device_create_file ( & spi - > dev , & dev_attr_name ) ) ) {
2006-06-12 22:00:05 +02:00
dev_dbg ( & spi - > dev , " device_create_file failure. \n " ) ;
goto out_dev_create_file_failed ;
}
return 0 ;
out_dev_create_file_failed :
2007-08-17 17:31:35 +02:00
device_remove_file ( & spi - > dev , & dev_attr_temp1_input ) ;
2007-08-20 13:46:20 -07:00
hwmon_device_unregister ( p_lm70 - > hwmon_dev ) ;
2006-06-12 22:00:05 +02:00
out_dev_reg_failed :
dev_set_drvdata ( & spi - > dev , NULL ) ;
kfree ( p_lm70 ) ;
return status ;
}
2007-02-14 21:15:04 +01:00
static int __devexit lm70_remove ( struct spi_device * spi )
2006-06-12 22:00:05 +02:00
{
struct lm70 * p_lm70 = dev_get_drvdata ( & spi - > dev ) ;
device_remove_file ( & spi - > dev , & dev_attr_temp1_input ) ;
2007-08-17 17:31:35 +02:00
device_remove_file ( & spi - > dev , & dev_attr_name ) ;
2007-08-20 13:46:20 -07:00
hwmon_device_unregister ( p_lm70 - > hwmon_dev ) ;
2006-06-12 22:00:05 +02:00
dev_set_drvdata ( & spi - > dev , NULL ) ;
kfree ( p_lm70 ) ;
return 0 ;
}
static struct spi_driver lm70_driver = {
. driver = {
. name = " lm70 " ,
. owner = THIS_MODULE ,
} ,
. probe = lm70_probe ,
. remove = __devexit_p ( lm70_remove ) ,
} ;
static int __init init_lm70 ( void )
{
return spi_register_driver ( & lm70_driver ) ;
}
static void __exit cleanup_lm70 ( void )
{
spi_unregister_driver ( & lm70_driver ) ;
}
module_init ( init_lm70 ) ;
module_exit ( cleanup_lm70 ) ;
MODULE_AUTHOR ( " Kaiwan N Billimoria " ) ;
MODULE_DESCRIPTION ( " National Semiconductor LM70 Linux driver " ) ;
MODULE_LICENSE ( " GPL " ) ;