1 /* Copyright (c) 2010 Christoph Mair <christoph.mair@gmail.com>
3 This driver supports the bmp085 digital barometric pressure
4 and temperature sensor from Bosch Sensortec. The datasheet
5 is available from their website:
6 http://www.bosch-sensortec.com/content/language1/downloads/BST-BMP085-DS000-05.pdf
8 A pressure measurement is issued by reading from pressure0_input.
9 The return value ranges from 30000 to 110000 pascal with a resulution
10 of 1 pascal (0.01 millibar) which enables measurements from 9000m above
11 to 500m below sea level.
13 The temperature can be read from temp0_input. Values range from
14 -400 to 850 representing the ambient temperature in degree celsius
15 multiplied by 10.The resolution is 0.1 celsius.
17 Because ambient pressure is temperature dependent, a temperature
18 measurement will be executed automatically even if the user is reading
19 from pressure0_input. This happens if the last temperature measurement
20 has been executed more then one second ago.
22 To decrease RMS noise from pressure measurements, the bmp085 can
23 autonomously calculate the average of up to eight samples. This is
24 set up by writing to the oversampling sysfs file. Accepted values
25 are 0, 1, 2 and 3. 2^x when x is the value written to this file
26 specifies the number of samples used to calculate the ambient pressure.
27 RMS noise is specified with six pascal (without averaging) and decreases
28 down to 3 pascal when using an oversampling setting of 3.
30 This program is free software; you can redistribute it and/or modify
31 it under the terms of the GNU General Public License as published by
32 the Free Software Foundation; either version 2 of the License, or
33 (at your option) any later version.
35 This program is distributed in the hope that it will be useful,
36 but WITHOUT ANY WARRANTY; without even the implied warranty of
37 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
38 GNU General Public License for more details.
40 You should have received a copy of the GNU General Public License
41 along with this program; if not, write to the Free Software
42 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
46 #include <linux/module.h>
47 #include <linux/init.h>
48 #include <linux/i2c.h>
49 #include <linux/slab.h>
50 #include <linux/delay.h>
53 #define BMP085_I2C_ADDRESS 0x77
54 #define BMP085_CHIP_ID 0x55
56 #define BMP085_CALIBRATION_DATA_START 0xAA
57 #define BMP085_CALIBRATION_DATA_LENGTH 11 /* 16 bit values */
58 #define BMP085_CHIP_ID_REG 0xD0
59 #define BMP085_VERSION_REG 0xD1
60 #define BMP085_CTRL_REG 0xF4
61 #define BMP085_TEMP_MEASUREMENT 0x2E
62 #define BMP085_PRESSURE_MEASUREMENT 0x34
63 #define BMP085_CONVERSION_REGISTER_MSB 0xF6
64 #define BMP085_CONVERSION_REGISTER_LSB 0xF7
65 #define BMP085_CONVERSION_REGISTER_XLSB 0xF8
66 #define BMP085_TEMP_CONVERSION_TIME 5
68 #define BMP085_CLIENT_NAME "bmp085"
71 static const unsigned short normal_i2c
[] = { BMP085_I2C_ADDRESS
,
74 struct bmp085_calibration_data
{
82 /* Each client has this additional data */
84 struct i2c_client
*client
;
86 struct bmp085_calibration_data calibration
;
89 unsigned char oversampling_setting
;
90 u32 last_temp_measurement
;
91 s32 b6
; /* calculated temperature correction coefficient */
95 static s32
bmp085_read_calibration_data(struct i2c_client
*client
)
97 u16 tmp
[BMP085_CALIBRATION_DATA_LENGTH
];
98 struct bmp085_data
*data
= i2c_get_clientdata(client
);
99 struct bmp085_calibration_data
*cali
= &(data
->calibration
);
100 s32 status
= i2c_smbus_read_i2c_block_data(client
,
101 BMP085_CALIBRATION_DATA_START
,
102 BMP085_CALIBRATION_DATA_LENGTH
*sizeof(u16
),
107 if (status
!= BMP085_CALIBRATION_DATA_LENGTH
*sizeof(u16
))
110 cali
->AC1
= be16_to_cpu(tmp
[0]);
111 cali
->AC2
= be16_to_cpu(tmp
[1]);
112 cali
->AC3
= be16_to_cpu(tmp
[2]);
113 cali
->AC4
= be16_to_cpu(tmp
[3]);
114 cali
->AC5
= be16_to_cpu(tmp
[4]);
115 cali
->AC6
= be16_to_cpu(tmp
[5]);
116 cali
->B1
= be16_to_cpu(tmp
[6]);
117 cali
->B2
= be16_to_cpu(tmp
[7]);
118 cali
->MB
= be16_to_cpu(tmp
[8]);
119 cali
->MC
= be16_to_cpu(tmp
[9]);
120 cali
->MD
= be16_to_cpu(tmp
[10]);
125 static s32
bmp085_update_raw_temperature(struct bmp085_data
*data
)
130 mutex_lock(&data
->lock
);
131 status
= i2c_smbus_write_byte_data(data
->client
, BMP085_CTRL_REG
,
132 BMP085_TEMP_MEASUREMENT
);
134 dev_err(&data
->client
->dev
,
135 "Error while requesting temperature measurement.\n");
138 msleep(BMP085_TEMP_CONVERSION_TIME
);
140 status
= i2c_smbus_read_i2c_block_data(data
->client
,
141 BMP085_CONVERSION_REGISTER_MSB
, sizeof(tmp
), (u8
*)&tmp
);
144 if (status
!= sizeof(tmp
)) {
145 dev_err(&data
->client
->dev
,
146 "Error while reading temperature measurement result\n");
150 data
->raw_temperature
= be16_to_cpu(tmp
);
151 data
->last_temp_measurement
= jiffies
;
152 status
= 0; /* everything ok, return 0 */
155 mutex_unlock(&data
->lock
);
159 static s32
bmp085_update_raw_pressure(struct bmp085_data
*data
)
164 mutex_lock(&data
->lock
);
165 status
= i2c_smbus_write_byte_data(data
->client
, BMP085_CTRL_REG
,
166 BMP085_PRESSURE_MEASUREMENT
+ (data
->oversampling_setting
<<6));
168 dev_err(&data
->client
->dev
,
169 "Error while requesting pressure measurement.\n");
173 /* wait for the end of conversion */
174 msleep(2+(3 << data
->oversampling_setting
));
176 /* copy data into a u32 (4 bytes), but skip the first byte. */
177 status
= i2c_smbus_read_i2c_block_data(data
->client
,
178 BMP085_CONVERSION_REGISTER_MSB
, 3, ((u8
*)&tmp
)+1);
182 dev_err(&data
->client
->dev
,
183 "Error while reading pressure measurement results\n");
187 data
->raw_pressure
= be32_to_cpu((tmp
));
188 data
->raw_pressure
>>= (8-data
->oversampling_setting
);
189 status
= 0; /* everything ok, return 0 */
192 mutex_unlock(&data
->lock
);
198 * This function starts the temperature measurement and returns the value
199 * in tenth of a degree celsius.
201 static s32
bmp085_get_temperature(struct bmp085_data
*data
, int *temperature
)
203 struct bmp085_calibration_data
*cali
= &data
->calibration
;
207 status
= bmp085_update_raw_temperature(data
);
211 x1
= ((data
->raw_temperature
- cali
->AC6
) * cali
->AC5
) >> 15;
212 x2
= (cali
->MC
<< 11) / (x1
+ cali
->MD
);
213 data
->b6
= x1
+ x2
- 4000;
214 /* if NULL just update b6. Used for pressure only measurements */
215 if (temperature
!= NULL
)
216 *temperature
= (x1
+x2
+8) >> 4;
223 * This function starts the pressure measurement and returns the value
224 * in millibar. Since the pressure depends on the ambient temperature,
225 * a temperature measurement is executed if the last known value is older
228 static s32
bmp085_get_pressure(struct bmp085_data
*data
, int *pressure
)
230 struct bmp085_calibration_data
*cali
= &data
->calibration
;
236 /* alt least every second force an update of the ambient temperature */
237 if (data
->last_temp_measurement
+ 1*HZ
< jiffies
) {
238 status
= bmp085_get_temperature(data
, NULL
);
243 status
= bmp085_update_raw_pressure(data
);
247 x1
= (data
->b6
* data
->b6
) >> 12;
251 x2
= cali
->AC2
* data
->b6
;
256 b3
= (((((s32
)cali
->AC1
) * 4 + x3
) << data
->oversampling_setting
) + 2);
259 x1
= (cali
->AC3
* data
->b6
) >> 13;
260 x2
= (cali
->B1
* ((data
->b6
* data
->b6
) >> 12)) >> 16;
261 x3
= (x1
+ x2
+ 2) >> 2;
262 b4
= (cali
->AC4
* (u32
)(x3
+ 32768)) >> 15;
264 b7
= ((u32
)data
->raw_pressure
- b3
) *
265 (50000 >> data
->oversampling_setting
);
266 p
= ((b7
< 0x80000000) ? ((b7
<< 1) / b4
) : ((b7
/ b4
) * 2));
270 x1
= (x1
* 3038) >> 16;
271 x2
= (-7357 * p
) >> 16;
272 p
+= (x1
+ x2
+ 3791) >> 4;
281 * This function sets the chip-internal oversampling. Valid values are 0..3.
282 * The chip will use 2^oversampling samples for internal averaging.
283 * This influences the measurement time and the accuracy; larger values
284 * increase both. The datasheet gives on overview on how measurement time,
285 * accuracy and noise correlate.
287 static void bmp085_set_oversampling(struct bmp085_data
*data
,
288 unsigned char oversampling
)
290 if (oversampling
> 3)
292 data
->oversampling_setting
= oversampling
;
296 * Returns the currently selected oversampling. Range: 0..3
298 static unsigned char bmp085_get_oversampling(struct bmp085_data
*data
)
300 return data
->oversampling_setting
;
303 /* sysfs callbacks */
304 static ssize_t
set_oversampling(struct device
*dev
,
305 struct device_attribute
*attr
,
306 const char *buf
, size_t count
)
308 struct i2c_client
*client
= to_i2c_client(dev
);
309 struct bmp085_data
*data
= i2c_get_clientdata(client
);
310 unsigned long oversampling
;
311 int success
= strict_strtoul(buf
, 10, &oversampling
);
313 bmp085_set_oversampling(data
, oversampling
);
319 static ssize_t
show_oversampling(struct device
*dev
,
320 struct device_attribute
*attr
, char *buf
)
322 struct i2c_client
*client
= to_i2c_client(dev
);
323 struct bmp085_data
*data
= i2c_get_clientdata(client
);
324 return sprintf(buf
, "%u\n", bmp085_get_oversampling(data
));
326 static DEVICE_ATTR(oversampling
, S_IWUSR
| S_IRUGO
,
327 show_oversampling
, set_oversampling
);
330 static ssize_t
show_temperature(struct device
*dev
,
331 struct device_attribute
*attr
, char *buf
)
335 struct i2c_client
*client
= to_i2c_client(dev
);
336 struct bmp085_data
*data
= i2c_get_clientdata(client
);
338 status
= bmp085_get_temperature(data
, &temperature
);
342 return sprintf(buf
, "%d\n", temperature
);
344 static DEVICE_ATTR(temp0_input
, S_IRUGO
, show_temperature
, NULL
);
347 static ssize_t
show_pressure(struct device
*dev
,
348 struct device_attribute
*attr
, char *buf
)
352 struct i2c_client
*client
= to_i2c_client(dev
);
353 struct bmp085_data
*data
= i2c_get_clientdata(client
);
355 status
= bmp085_get_pressure(data
, &pressure
);
359 return sprintf(buf
, "%d\n", pressure
);
361 static DEVICE_ATTR(pressure0_input
, S_IRUGO
, show_pressure
, NULL
);
364 static struct attribute
*bmp085_attributes
[] = {
365 &dev_attr_temp0_input
.attr
,
366 &dev_attr_pressure0_input
.attr
,
367 &dev_attr_oversampling
.attr
,
371 static const struct attribute_group bmp085_attr_group
= {
372 .attrs
= bmp085_attributes
,
375 static int bmp085_detect(struct i2c_client
*client
, struct i2c_board_info
*info
)
377 if (client
->addr
!= BMP085_I2C_ADDRESS
)
380 if (i2c_smbus_read_byte_data(client
, BMP085_CHIP_ID_REG
) != BMP085_CHIP_ID
)
386 static int bmp085_init_client(struct i2c_client
*client
)
388 unsigned char version
;
390 struct bmp085_data
*data
= i2c_get_clientdata(client
);
391 data
->client
= client
;
392 status
= bmp085_read_calibration_data(client
);
395 version
= i2c_smbus_read_byte_data(client
, BMP085_VERSION_REG
);
396 data
->last_temp_measurement
= 0;
397 data
->oversampling_setting
= 3;
398 mutex_init(&data
->lock
);
399 dev_info(&data
->client
->dev
, "BMP085 ver. %d.%d found.\n",
400 (version
& 0x0F), (version
& 0xF0) >> 4);
405 static int __devinit
bmp085_probe(struct i2c_client
*client
,
406 const struct i2c_device_id
*id
)
408 struct bmp085_data
*data
;
411 data
= kzalloc(sizeof(struct bmp085_data
), GFP_KERNEL
);
417 /* default settings after POR */
418 data
->oversampling_setting
= 0x00;
420 i2c_set_clientdata(client
, data
);
422 /* Initialize the BMP085 chip */
423 err
= bmp085_init_client(client
);
427 /* Register sysfs hooks */
428 err
= sysfs_create_group(&client
->dev
.kobj
, &bmp085_attr_group
);
432 dev_info(&data
->client
->dev
, "Successfully initialized bmp085!\n");
441 static int __devexit
bmp085_remove(struct i2c_client
*client
)
443 sysfs_remove_group(&client
->dev
.kobj
, &bmp085_attr_group
);
444 kfree(i2c_get_clientdata(client
));
448 static const struct i2c_device_id bmp085_id
[] = {
452 MODULE_DEVICE_TABLE(i2c
, bmp085_id
);
454 static struct i2c_driver bmp085_driver
= {
456 .owner
= THIS_MODULE
,
459 .id_table
= bmp085_id
,
460 .probe
= bmp085_probe
,
461 .remove
= __devexit_p(bmp085_remove
),
463 .detect
= bmp085_detect
,
464 .address_list
= normal_i2c
467 static int __init
bmp085_init(void)
469 return i2c_add_driver(&bmp085_driver
);
472 static void __exit
bmp085_exit(void)
474 i2c_del_driver(&bmp085_driver
);
478 MODULE_AUTHOR("Christoph Mair <christoph.mair@gmail.com");
479 MODULE_DESCRIPTION("BMP085 driver");
480 MODULE_LICENSE("GPL");
482 module_init(bmp085_init
);
483 module_exit(bmp085_exit
);