2010-08-10 04:20:28 +04:00
/* Copyright (c) 2010 Christoph Mair <christoph.mair@gmail.com>
2012-04-10 00:16:15 +04:00
* Copyright ( c ) 2012 Bosch Sensortec GmbH
* Copyright ( c ) 2012 Unixphere AB
*
2012-04-10 00:16:17 +04:00
* This driver supports the bmp085 and bmp18x digital barometric pressure
* and temperature sensors from Bosch Sensortec . The datasheets
* are available from their website :
2012-04-10 00:16:15 +04:00
* http : //www.bosch-sensortec.com/content/language1/downloads/BST-BMP085-DS000-05.pdf
2012-04-10 00:16:17 +04:00
* http : //www.bosch-sensortec.com/content/language1/downloads/BST-BMP180-DS000-07.pdf
2012-04-10 00:16:15 +04:00
*
* A pressure measurement is issued by reading from pressure0_input .
* The return value ranges from 30000 to 110000 pascal with a resulution
* of 1 pascal ( 0.01 millibar ) which enables measurements from 9000 m above
* to 500 m below sea level .
*
* The temperature can be read from temp0_input . Values range from
* - 400 to 850 representing the ambient temperature in degree celsius
* multiplied by 10. The resolution is 0.1 celsius .
*
* Because ambient pressure is temperature dependent , a temperature
* measurement will be executed automatically even if the user is reading
* from pressure0_input . This happens if the last temperature measurement
* has been executed more then one second ago .
*
* To decrease RMS noise from pressure measurements , the bmp085 can
* autonomously calculate the average of up to eight samples . This is
* set up by writing to the oversampling sysfs file . Accepted values
* are 0 , 1 , 2 and 3. 2 ^ x when x is the value written to this file
* specifies the number of samples used to calculate the ambient pressure .
* RMS noise is specified with six pascal ( without averaging ) and decreases
* down to 3 pascal when using an oversampling setting of 3.
*
* 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 .
*/
2010-08-10 04:20:28 +04:00
# include <linux/module.h>
2012-04-10 00:16:15 +04:00
# include <linux/device.h>
2010-08-10 04:20:28 +04:00
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/delay.h>
2012-04-10 00:16:16 +04:00
# include <linux/of.h>
2012-04-10 00:16:17 +04:00
# include "bmp085.h"
2010-08-10 04:20:28 +04:00
# define BMP085_CHIP_ID 0x55
# define BMP085_CALIBRATION_DATA_START 0xAA
# define BMP085_CALIBRATION_DATA_LENGTH 11 /* 16 bit values */
# define BMP085_CHIP_ID_REG 0xD0
# define BMP085_CTRL_REG 0xF4
# define BMP085_TEMP_MEASUREMENT 0x2E
# define BMP085_PRESSURE_MEASUREMENT 0x34
# define BMP085_CONVERSION_REGISTER_MSB 0xF6
# define BMP085_CONVERSION_REGISTER_LSB 0xF7
# define BMP085_CONVERSION_REGISTER_XLSB 0xF8
# define BMP085_TEMP_CONVERSION_TIME 5
struct bmp085_calibration_data {
s16 AC1 , AC2 , AC3 ;
u16 AC4 , AC5 , AC6 ;
s16 B1 , B2 ;
s16 MB , MC , MD ;
} ;
struct bmp085_data {
2012-04-10 00:16:17 +04:00
struct device * dev ;
struct regmap * regmap ;
2012-04-10 00:16:15 +04:00
struct mutex lock ;
struct bmp085_calibration_data calibration ;
u8 oversampling_setting ;
u32 raw_temperature ;
u32 raw_pressure ;
u32 temp_measurement_period ;
2012-02-25 13:28:12 +04:00
unsigned long last_temp_measurement ;
2012-04-10 00:16:16 +04:00
u8 chip_id ;
2012-04-10 00:16:15 +04:00
s32 b6 ; /* calculated temperature correction coefficient */
2010-08-10 04:20:28 +04:00
} ;
2012-04-10 00:16:17 +04:00
static s32 bmp085_read_calibration_data ( struct bmp085_data * data )
2010-08-10 04:20:28 +04:00
{
u16 tmp [ BMP085_CALIBRATION_DATA_LENGTH ] ;
struct bmp085_calibration_data * cali = & ( data - > calibration ) ;
2012-04-10 00:16:17 +04:00
s32 status = regmap_bulk_read ( data - > regmap ,
BMP085_CALIBRATION_DATA_START , ( u8 * ) tmp ,
( BMP085_CALIBRATION_DATA_LENGTH < < 1 ) ) ;
2010-08-10 04:20:28 +04:00
if ( status < 0 )
return status ;
cali - > AC1 = be16_to_cpu ( tmp [ 0 ] ) ;
cali - > AC2 = be16_to_cpu ( tmp [ 1 ] ) ;
cali - > AC3 = be16_to_cpu ( tmp [ 2 ] ) ;
cali - > AC4 = be16_to_cpu ( tmp [ 3 ] ) ;
cali - > AC5 = be16_to_cpu ( tmp [ 4 ] ) ;
cali - > AC6 = be16_to_cpu ( tmp [ 5 ] ) ;
cali - > B1 = be16_to_cpu ( tmp [ 6 ] ) ;
cali - > B2 = be16_to_cpu ( tmp [ 7 ] ) ;
cali - > MB = be16_to_cpu ( tmp [ 8 ] ) ;
cali - > MC = be16_to_cpu ( tmp [ 9 ] ) ;
cali - > MD = be16_to_cpu ( tmp [ 10 ] ) ;
return 0 ;
}
static s32 bmp085_update_raw_temperature ( struct bmp085_data * data )
{
u16 tmp ;
s32 status ;
mutex_lock ( & data - > lock ) ;
2012-04-10 00:16:17 +04:00
status = regmap_write ( data - > regmap , BMP085_CTRL_REG ,
BMP085_TEMP_MEASUREMENT ) ;
2012-04-10 00:16:15 +04:00
if ( status < 0 ) {
2012-04-10 00:16:17 +04:00
dev_err ( data - > dev ,
2010-08-10 04:20:28 +04:00
" Error while requesting temperature measurement. \n " ) ;
goto exit ;
}
msleep ( BMP085_TEMP_CONVERSION_TIME ) ;
2012-04-10 00:16:17 +04:00
status = regmap_bulk_read ( data - > regmap , BMP085_CONVERSION_REGISTER_MSB ,
& tmp , sizeof ( tmp ) ) ;
if ( status < 0 ) {
dev_err ( data - > dev ,
2010-08-10 04:20:28 +04:00
" Error while reading temperature measurement result \n " ) ;
goto exit ;
}
data - > raw_temperature = be16_to_cpu ( tmp ) ;
data - > last_temp_measurement = jiffies ;
status = 0 ; /* everything ok, return 0 */
exit :
mutex_unlock ( & data - > lock ) ;
return status ;
}
static s32 bmp085_update_raw_pressure ( struct bmp085_data * data )
{
u32 tmp = 0 ;
s32 status ;
mutex_lock ( & data - > lock ) ;
2012-04-10 00:16:17 +04:00
status = regmap_write ( data - > regmap , BMP085_CTRL_REG ,
2012-04-10 00:16:15 +04:00
BMP085_PRESSURE_MEASUREMENT +
( data - > oversampling_setting < < 6 ) ) ;
if ( status < 0 ) {
2012-04-10 00:16:17 +04:00
dev_err ( data - > dev ,
2010-08-10 04:20:28 +04:00
" Error while requesting pressure measurement. \n " ) ;
goto exit ;
}
/* wait for the end of conversion */
msleep ( 2 + ( 3 < < data - > oversampling_setting ) ) ;
/* copy data into a u32 (4 bytes), but skip the first byte. */
2012-04-10 00:16:17 +04:00
status = regmap_bulk_read ( data - > regmap , BMP085_CONVERSION_REGISTER_MSB ,
( ( u8 * ) & tmp ) + 1 , 3 ) ;
if ( status < 0 ) {
dev_err ( data - > dev ,
2010-08-10 04:20:28 +04:00
" Error while reading pressure measurement results \n " ) ;
goto exit ;
}
data - > raw_pressure = be32_to_cpu ( ( tmp ) ) ;
data - > raw_pressure > > = ( 8 - data - > oversampling_setting ) ;
status = 0 ; /* everything ok, return 0 */
exit :
mutex_unlock ( & data - > lock ) ;
return status ;
}
/*
* This function starts the temperature measurement and returns the value
* in tenth of a degree celsius .
*/
static s32 bmp085_get_temperature ( struct bmp085_data * data , int * temperature )
{
struct bmp085_calibration_data * cali = & data - > calibration ;
long x1 , x2 ;
int status ;
status = bmp085_update_raw_temperature ( data ) ;
2012-04-10 00:16:15 +04:00
if ( status < 0 )
2010-08-10 04:20:28 +04:00
goto exit ;
x1 = ( ( data - > raw_temperature - cali - > AC6 ) * cali - > AC5 ) > > 15 ;
x2 = ( cali - > MC < < 11 ) / ( x1 + cali - > MD ) ;
data - > b6 = x1 + x2 - 4000 ;
/* if NULL just update b6. Used for pressure only measurements */
if ( temperature ! = NULL )
* temperature = ( x1 + x2 + 8 ) > > 4 ;
exit :
2011-12-18 02:52:27 +04:00
return status ;
2010-08-10 04:20:28 +04:00
}
/*
* This function starts the pressure measurement and returns the value
* in millibar . Since the pressure depends on the ambient temperature ,
2012-04-10 00:16:15 +04:00
* a temperature measurement is executed according to the given temperature
* measurement period ( default is 1 sec boundary ) . This period could vary
* and needs to be adjusted according to the sensor environment , i . e . if big
* temperature variations then the temperature needs to be read out often .
2010-08-10 04:20:28 +04:00
*/
static s32 bmp085_get_pressure ( struct bmp085_data * data , int * pressure )
{
struct bmp085_calibration_data * cali = & data - > calibration ;
s32 x1 , x2 , x3 , b3 ;
u32 b4 , b7 ;
s32 p ;
int status ;
/* alt least every second force an update of the ambient temperature */
2012-04-10 00:16:15 +04:00
if ( ( data - > last_temp_measurement = = 0 ) | |
time_is_before_jiffies ( data - > last_temp_measurement + 1 * HZ ) ) {
2010-08-10 04:20:28 +04:00
status = bmp085_get_temperature ( data , NULL ) ;
2012-04-10 00:16:15 +04:00
if ( status < 0 )
return status ;
2010-08-10 04:20:28 +04:00
}
status = bmp085_update_raw_pressure ( data ) ;
2012-04-10 00:16:15 +04:00
if ( status < 0 )
return status ;
2010-08-10 04:20:28 +04:00
x1 = ( data - > b6 * data - > b6 ) > > 12 ;
x1 * = cali - > B2 ;
x1 > > = 11 ;
x2 = cali - > AC2 * data - > b6 ;
x2 > > = 11 ;
x3 = x1 + x2 ;
b3 = ( ( ( ( ( s32 ) cali - > AC1 ) * 4 + x3 ) < < data - > oversampling_setting ) + 2 ) ;
b3 > > = 2 ;
x1 = ( cali - > AC3 * data - > b6 ) > > 13 ;
x2 = ( cali - > B1 * ( ( data - > b6 * data - > b6 ) > > 12 ) ) > > 16 ;
x3 = ( x1 + x2 + 2 ) > > 2 ;
b4 = ( cali - > AC4 * ( u32 ) ( x3 + 32768 ) ) > > 15 ;
b7 = ( ( u32 ) data - > raw_pressure - b3 ) *
( 50000 > > data - > oversampling_setting ) ;
p = ( ( b7 < 0x80000000 ) ? ( ( b7 < < 1 ) / b4 ) : ( ( b7 / b4 ) * 2 ) ) ;
x1 = p > > 8 ;
x1 * = x1 ;
x1 = ( x1 * 3038 ) > > 16 ;
x2 = ( - 7357 * p ) > > 16 ;
p + = ( x1 + x2 + 3791 ) > > 4 ;
* pressure = p ;
2012-04-10 00:16:15 +04:00
return 0 ;
2010-08-10 04:20:28 +04:00
}
/*
* This function sets the chip - internal oversampling . Valid values are 0. .3 .
* The chip will use 2 ^ oversampling samples for internal averaging .
* This influences the measurement time and the accuracy ; larger values
2012-04-10 00:16:15 +04:00
* increase both . The datasheet gives an overview on how measurement time ,
2010-08-10 04:20:28 +04:00
* accuracy and noise correlate .
*/
static void bmp085_set_oversampling ( struct bmp085_data * data ,
unsigned char oversampling )
{
if ( oversampling > 3 )
oversampling = 3 ;
data - > oversampling_setting = oversampling ;
}
/*
* Returns the currently selected oversampling . Range : 0. .3
*/
static unsigned char bmp085_get_oversampling ( struct bmp085_data * data )
{
return data - > oversampling_setting ;
}
/* sysfs callbacks */
static ssize_t set_oversampling ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
2012-04-10 00:16:17 +04:00
struct bmp085_data * data = dev_get_drvdata ( dev ) ;
2010-08-10 04:20:28 +04:00
unsigned long oversampling ;
2012-04-10 00:16:15 +04:00
int err = kstrtoul ( buf , 10 , & oversampling ) ;
if ( err = = 0 ) {
mutex_lock ( & data - > lock ) ;
2010-08-10 04:20:28 +04:00
bmp085_set_oversampling ( data , oversampling ) ;
2012-04-10 00:16:15 +04:00
mutex_unlock ( & data - > lock ) ;
2010-08-10 04:20:28 +04:00
return count ;
}
2012-04-10 00:16:15 +04:00
return err ;
2010-08-10 04:20:28 +04:00
}
static ssize_t show_oversampling ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
2012-04-10 00:16:17 +04:00
struct bmp085_data * data = dev_get_drvdata ( dev ) ;
2012-04-10 00:16:15 +04:00
2010-08-10 04:20:28 +04:00
return sprintf ( buf , " %u \n " , bmp085_get_oversampling ( data ) ) ;
}
static DEVICE_ATTR ( oversampling , S_IWUSR | S_IRUGO ,
show_oversampling , set_oversampling ) ;
static ssize_t show_temperature ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
int temperature ;
int status ;
2012-04-10 00:16:17 +04:00
struct bmp085_data * data = dev_get_drvdata ( dev ) ;
2010-08-10 04:20:28 +04:00
status = bmp085_get_temperature ( data , & temperature ) ;
2012-04-10 00:16:15 +04:00
if ( status < 0 )
2010-08-10 04:20:28 +04:00
return status ;
else
return sprintf ( buf , " %d \n " , temperature ) ;
}
static DEVICE_ATTR ( temp0_input , S_IRUGO , show_temperature , NULL ) ;
static ssize_t show_pressure ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
int pressure ;
int status ;
2012-04-10 00:16:17 +04:00
struct bmp085_data * data = dev_get_drvdata ( dev ) ;
2010-08-10 04:20:28 +04:00
status = bmp085_get_pressure ( data , & pressure ) ;
2012-04-10 00:16:15 +04:00
if ( status < 0 )
2010-08-10 04:20:28 +04:00
return status ;
else
return sprintf ( buf , " %d \n " , pressure ) ;
}
static DEVICE_ATTR ( pressure0_input , S_IRUGO , show_pressure , NULL ) ;
static struct attribute * bmp085_attributes [ ] = {
& dev_attr_temp0_input . attr ,
& dev_attr_pressure0_input . attr ,
& dev_attr_oversampling . attr ,
NULL
} ;
static const struct attribute_group bmp085_attr_group = {
. attrs = bmp085_attributes ,
} ;
2012-04-10 00:16:17 +04:00
int bmp085_detect ( struct device * dev )
2010-08-10 04:20:28 +04:00
{
2012-04-10 00:16:17 +04:00
struct bmp085_data * data = dev_get_drvdata ( dev ) ;
unsigned int id ;
int ret ;
ret = regmap_read ( data - > regmap , BMP085_CHIP_ID_REG , & id ) ;
if ( ret < 0 )
return ret ;
2010-08-10 04:20:28 +04:00
2012-04-10 00:16:17 +04:00
if ( id ! = data - > chip_id )
2010-08-10 04:20:28 +04:00
return - ENODEV ;
return 0 ;
}
2012-04-10 00:16:17 +04:00
EXPORT_SYMBOL_GPL ( bmp085_detect ) ;
2010-08-10 04:20:28 +04:00
2012-04-10 00:16:17 +04:00
static void __init bmp085_get_of_properties ( struct bmp085_data * data )
2012-04-10 00:16:16 +04:00
{
# ifdef CONFIG_OF
2012-04-10 00:16:17 +04:00
struct device_node * np = data - > dev - > of_node ;
2012-04-10 00:16:16 +04:00
u32 prop ;
if ( ! np )
return ;
if ( ! of_property_read_u32 ( np , " chip-id " , & prop ) )
data - > chip_id = prop & 0xff ;
if ( ! of_property_read_u32 ( np , " temp-measurement-period " , & prop ) )
data - > temp_measurement_period = ( prop / 100 ) * HZ ;
if ( ! of_property_read_u32 ( np , " default-oversampling " , & prop ) )
data - > oversampling_setting = prop & 0xff ;
# endif
}
2012-04-10 00:16:17 +04:00
static int bmp085_init_client ( struct bmp085_data * data )
2010-08-10 04:20:28 +04:00
{
2012-04-10 00:16:17 +04:00
int status = bmp085_read_calibration_data ( data ) ;
2012-04-10 00:16:15 +04:00
if ( status < 0 )
return status ;
2012-04-10 00:16:16 +04:00
/* default settings */
data - > chip_id = BMP085_CHIP_ID ;
2010-08-10 04:20:28 +04:00
data - > last_temp_measurement = 0 ;
2012-04-10 00:16:15 +04:00
data - > temp_measurement_period = 1 * HZ ;
2010-08-10 04:20:28 +04:00
data - > oversampling_setting = 3 ;
2012-04-10 00:16:16 +04:00
2012-04-10 00:16:17 +04:00
bmp085_get_of_properties ( data ) ;
2012-04-10 00:16:16 +04:00
2010-08-10 04:20:28 +04:00
mutex_init ( & data - > lock ) ;
2012-04-10 00:16:15 +04:00
return 0 ;
2010-08-10 04:20:28 +04:00
}
2012-04-10 00:16:17 +04:00
struct regmap_config bmp085_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8
} ;
EXPORT_SYMBOL_GPL ( bmp085_regmap_config ) ;
__devinit int bmp085_probe ( struct device * dev , struct regmap * regmap )
2010-08-10 04:20:28 +04:00
{
struct bmp085_data * data ;
int err = 0 ;
data = kzalloc ( sizeof ( struct bmp085_data ) , GFP_KERNEL ) ;
if ( ! data ) {
err = - ENOMEM ;
goto exit ;
}
2012-04-10 00:16:17 +04:00
dev_set_drvdata ( dev , data ) ;
data - > dev = dev ;
data - > regmap = regmap ;
2010-08-10 04:20:28 +04:00
/* Initialize the BMP085 chip */
2012-04-10 00:16:17 +04:00
err = bmp085_init_client ( data ) ;
2012-04-10 00:16:15 +04:00
if ( err < 0 )
2010-08-10 04:20:28 +04:00
goto exit_free ;
2012-04-10 00:16:17 +04:00
err = bmp085_detect ( dev ) ;
if ( err < 0 ) {
dev_err ( dev , " %s: chip_id failed! \n " , BMP085_NAME ) ;
goto exit_free ;
}
2010-08-10 04:20:28 +04:00
/* Register sysfs hooks */
2012-04-10 00:16:17 +04:00
err = sysfs_create_group ( & dev - > kobj , & bmp085_attr_group ) ;
2010-08-10 04:20:28 +04:00
if ( err )
goto exit_free ;
2012-04-10 00:16:17 +04:00
dev_info ( dev , " Successfully initialized %s! \n " , BMP085_NAME ) ;
2012-04-10 00:16:15 +04:00
return 0 ;
2010-08-10 04:20:28 +04:00
exit_free :
kfree ( data ) ;
exit :
return err ;
}
2012-04-10 00:16:17 +04:00
EXPORT_SYMBOL_GPL ( bmp085_probe ) ;
2010-08-10 04:20:28 +04:00
2012-04-10 00:16:17 +04:00
int bmp085_remove ( struct device * dev )
2010-08-10 04:20:28 +04:00
{
2012-04-10 00:16:17 +04:00
struct bmp085_data * data = dev_get_drvdata ( dev ) ;
2012-04-10 00:16:15 +04:00
2012-04-10 00:16:17 +04:00
sysfs_remove_group ( & data - > dev - > kobj , & bmp085_attr_group ) ;
2012-04-10 00:16:15 +04:00
kfree ( data ) ;
2010-08-10 04:20:28 +04:00
return 0 ;
}
2012-04-10 00:16:17 +04:00
EXPORT_SYMBOL_GPL ( bmp085_remove ) ;
2010-08-10 04:20:28 +04:00
2012-04-10 00:16:15 +04:00
MODULE_AUTHOR ( " Christoph Mair <christoph.mair@gmail.com> " ) ;
2010-08-10 04:20:28 +04:00
MODULE_DESCRIPTION ( " BMP085 driver " ) ;
MODULE_LICENSE ( " GPL " ) ;