The driver was developed by Christoph Mair <christoph.m...@gmail.com> out of
tree so far.

Signed-off-by: Stefan Schmidt <ste...@datenfreihafen.org>
---
 arch/arm/mach-s3c2440/mach-gta02.c |    3 +
 drivers/i2c/chips/Kconfig          |   11 ++
 drivers/i2c/chips/Makefile         |    1 +
 drivers/i2c/chips/bmp085.c         |  345 ++++++++++++++++++++++++++++++++++++
 4 files changed, 360 insertions(+), 0 deletions(-)
 create mode 100644 drivers/i2c/chips/bmp085.c

diff --git a/arch/arm/mach-s3c2440/mach-gta02.c 
b/arch/arm/mach-s3c2440/mach-gta02.c
index 4520d7b..153549e 100644
--- a/arch/arm/mach-s3c2440/mach-gta02.c
+++ b/arch/arm/mach-s3c2440/mach-gta02.c
@@ -928,6 +928,9 @@ static struct i2c_board_info gta02_i2c_devs[] __initdata = {
                .irq = GTA02_IRQ_PCF50633,
                .platform_data = &gta02_pcf_pdata,
        },
+       {
+               I2C_BOARD_INFO("bmp085", 0x77),
+       },
 };
 
 static struct s3c2410_nand_set gta02_nand_sets[] = {
diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
index a84179a..e59c8cb 100644
--- a/drivers/i2c/chips/Kconfig
+++ b/drivers/i2c/chips/Kconfig
@@ -224,4 +224,15 @@ config PCA9632
 
          This driver can also be built as a module.  If so, the module
          will be called pca9632.
+
+config SENSORS_BMP085
+       tristate "Bosch Sensortec BMP085 pressure driver"
+       depends on I2C
+       help
+         If you say yes here you get support for the Bosch Sensortec BMP085
+         pressure driver.
+
+         This driver can also be built as a module.  If so, the module
+         will be called bmp085.
+
 endmenu
diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
index b0dc12e..17c3107 100644
--- a/drivers/i2c/chips/Makefile
+++ b/drivers/i2c/chips/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
 obj-$(CONFIG_MCU_MPC8349EMITX) += mcu_mpc8349emitx.o
 obj-$(CONFIG_SENSORS_TSL256X)  += tsl256x.o
 obj-$(CONFIG_PCA9632)          += pca9632.o
+obj-$(CONFIG_SENSORS_BMP085)   += bmp085.o
 
 ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
 EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/i2c/chips/bmp085.c b/drivers/i2c/chips/bmp085.c
new file mode 100644
index 0000000..d4523ce
--- /dev/null
+++ b/drivers/i2c/chips/bmp085.c
@@ -0,0 +1,345 @@
+/*  Copyright (c) 2009  Christoph Mair <christoph.m...@gmail.com>
+
+    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 02139, USA.
+*/
+
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+
+
+
+#define BMP085_I2C_ADDRESS             0x77
+
+#define BMP085_CALIBRATION_DATA_START  0xAA
+#define BMP085_CALIBRATION_DATA_LENGTH 22
+
+#define BMP085_CHIP_ID_REG             0xD0
+#define BMP085_VERSION_REG             0xD1
+
+#define BMP085_CHIP_ID                 0x55 /* 85 */
+
+
+/* Addresses to scan: 0x77 */
+static const unsigned short normal_i2c[] = { 0x77, I2C_CLIENT_END };
+
+/* Insmod parameters */
+I2C_CLIENT_INSMOD_1(bmp085);
+
+struct bmp085_calibration_data {
+       s16 AC1, AC2, AC3;
+       u16 AC4, AC5, AC6;
+       s16 B1, B2;
+       s16 MB, MC, MD;
+};
+
+
+/* Each client has this additional data */
+struct bmp085_data {
+       unsigned char version;
+       struct bmp085_calibration_data calibration;
+       unsigned char oversampling_setting;
+       unsigned long next_temp_measurement;
+       long b6; // calculated temperature correction coefficient
+};
+
+static void bmp085_init_client(struct i2c_client *client);
+
+
+static s32 bmp085_get_calibration_data(struct i2c_client *client)
+{
+       u8 tmp[BMP085_CALIBRATION_DATA_LENGTH];
+       struct bmp085_data *data = i2c_get_clientdata(client);
+       struct bmp085_calibration_data *cali = &(data->calibration);
+       s32 status = i2c_smbus_read_i2c_block_data(client, 
BMP085_CALIBRATION_DATA_START,
+                                                  
BMP085_CALIBRATION_DATA_LENGTH, tmp);
+
+       cali->AC1 =  (tmp[0] <<8) | tmp[1];
+       cali->AC2 =  (tmp[2] <<8) | tmp[3];
+       cali->AC3 =  (tmp[4] <<8) | tmp[5];
+       cali->AC4 =  (tmp[6] <<8) | tmp[7];
+       cali->AC5 =  (tmp[8] <<8) | tmp[9];
+       cali->AC6 = (tmp[10] <<8) | tmp[11];
+
+       /*parameters B1,B2*/
+       cali->B1 =  (tmp[12] <<8) | tmp[13];
+       cali->B2 =  (tmp[14] <<8) | tmp[15];
+
+       /*parameters MB,MC,MD*/
+       cali->MB =  (tmp[16] <<8) | tmp[17];
+       cali->MC =  (tmp[18] <<8) | tmp[19];
+       cali->MD =  (tmp[20] <<8) | tmp[21];
+       return status;
+}
+
+
+static s32 bmp085_get_temperature(struct i2c_client *client)
+{
+       struct bmp085_data *data = i2c_get_clientdata(client);
+       u16 temperature = 0x00;
+       u8 tmp[2];
+       s32 status = i2c_smbus_write_byte_data(client, 0xF4, 0x2E);
+       if (status != 0) {
+               printk(KERN_INFO "bmp085: Error while requesting temperature 
measurement.\n");
+               return status;
+       }
+       msleep(5);
+
+       i2c_smbus_read_i2c_block_data(client, 0xF6, 2, tmp);
+       data->next_temp_measurement = jiffies + 1;/**HZ;*/ /* next temperature 
measurement is needed in one second */
+       temperature = (tmp[0] << 8) + tmp[1];
+       printk(KERN_INFO "temperature: %u\n", temperature);
+       return temperature;
+}
+
+
+static s32 bmp085_read_pressure(struct i2c_client *client, u32 *pressure)
+{
+       struct bmp085_data *data = i2c_get_clientdata(client);
+       u8 tmp[3];
+       s32 status = 0;
+       *pressure = 0;
+
+       status = i2c_smbus_write_byte_data(client, 0xF4, 0x34 + 
(data->oversampling_setting<<6));
+       if (status != 0) {
+               return status;
+       }
+       msleep(2+(3 << data->oversampling_setting<<1)); /* wait for the end of 
conversion */
+
+       status = i2c_smbus_read_i2c_block_data(client, 0xF6, 0x03, tmp);
+       /* swap positions to correct the MSB/LSB positions*/
+       *pressure = (tmp[0] << 16) | (tmp[1] << 8) | tmp[2];
+       *pressure = *pressure >> (8-data->oversampling_setting);
+       printk(KERN_INFO "pressure: %u\n", *pressure);
+       return status;
+}
+
+
+/* sysfs callbacks */
+static ssize_t set_oversampling(struct device *dev, struct device_attribute 
*attr, const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct bmp085_data *data = i2c_get_clientdata(client);
+       data->oversampling_setting = simple_strtoul(buf, NULL, 10);
+       if (data->oversampling_setting > 3) {
+               data->oversampling_setting = 3;
+       }
+       return count;
+}
+
+static ssize_t show_oversampling(struct device *dev, struct device_attribute 
*attr, char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct bmp085_data *data = i2c_get_clientdata(client);
+       return sprintf(buf, "%u\n", data->oversampling_setting);
+}
+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)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct bmp085_data *data = i2c_get_clientdata(client);
+       struct bmp085_calibration_data *cali = &data->calibration;
+
+       long raw_temp = bmp085_get_temperature(client);
+       long x1 = ((raw_temp - cali->AC6) * cali->AC5) >> 15;
+       long x2 = (cali->MC << 11) / (x1 + cali->MD);
+       data->b6 = x1 + x2 - 4000;
+//     printk(KERN_INFO "got raw temp: %ld; AC5: %d, AC6: %d, MC: %d, MD: 
%d\n", raw_temp, cali->AC5, cali->AC6, cali->MC, cali->MD);
+       return sprintf(buf, "%ld\n", (x1+x2+8) >> 4);
+}
+static DEVICE_ATTR(temperature, S_IRUGO, show_temperature, NULL);
+
+
+static ssize_t show_pressure(struct device *dev, struct device_attribute 
*attr, char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct bmp085_data *data = i2c_get_clientdata(client);
+       struct bmp085_calibration_data *cali = &data->calibration;
+       long x1, x2, x3, b3;
+       unsigned long b4, b7;
+       long p;
+       unsigned int raw_pressure;
+       if (time_after(jiffies, data->next_temp_measurement)) {
+               printk(KERN_INFO "new temp required: %lu v.s. %lu\n", jiffies, 
data->next_temp_measurement);
+               show_temperature(dev, attr, buf); /* read temperature and 
calculate coefficients */
+       }
+
+       bmp085_read_pressure(client, &raw_pressure);
+//     printk(KERN_INFO "raw_pressure: %ud, B6: %ld\n", raw_pressure, 
data->b6);
+
+       x1 = (data->b6 * data->b6) >> 12;
+       x1 *= cali->B2;
+       x1 >>= 11;
+
+       x2 = cali->AC2 * data->b6;
+       x2 >>= 11;
+
+       x3 = x1 + x2;
+
+       b3 = (((((long)cali->AC1) * 4 + x3) << data->oversampling_setting) + 2) 
>> 2;
+
+//     printk(KERN_INFO "x1: %ld, x2: %ld, x3: %ld, b3: %ld\n", x1, x2, x3, 
b3);
+       x1 = (cali->AC3 * data->b6) >> 13;
+       x2 = (cali->B1 * ((data->b6 * data->b6) >> 12)) >> 16;
+       x3 = (x1 + x2 + 2) >> 2;
+       b4 = (cali->AC4 * (unsigned long)(x3 + 32768)) >> 15;
+
+//     printk(KERN_INFO "x1: %ld, x2: %ld, x3: %ld, b4: %ld\n", x1, x2, x3, 
b4);
+       b7 = ((unsigned long)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;
+       return sprintf(buf, "%ld\n", p);
+}
+static DEVICE_ATTR(pressure, S_IRUGO, show_pressure, NULL);
+
+
+static struct attribute *bmp085_attributes[] = {
+       &dev_attr_temperature.attr,
+       &dev_attr_pressure.attr,
+       &dev_attr_oversampling.attr,
+       NULL
+};
+
+static const struct attribute_group bmp085_attr_group = {
+       .attrs = bmp085_attributes,
+};
+
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int bmp085_detect(struct i2c_client *client, int kind, struct 
i2c_board_info *info)
+{
+       struct i2c_adapter *adapter = client->adapter;
+       const char *client_name;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
+               return -ENODEV;
+
+       if (client->addr == BMP085_I2C_ADDRESS) {
+               if (i2c_smbus_read_byte_data(client, BMP085_CHIP_ID_REG) != 
BMP085_CHIP_ID) {
+                       goto exit;
+               }
+
+               /* Chip identified */
+               client_name = "bmp085";
+               strlcpy(info->type, client_name, I2C_NAME_SIZE);
+               printk(KERN_INFO "BMP085 found!\n");
+               return 0;
+       }
+
+      exit:
+       printk(KERN_INFO "BMP085 not found at address %x!\n", client->addr);
+       return -ENODEV;
+}
+
+static int bmp085_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       struct bmp085_data *data;
+       int err;
+
+       printk(KERN_INFO "bmp085 probe!\n");
+
+       data = kzalloc(sizeof(struct bmp085_data), GFP_KERNEL);
+       if (!data) {
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       /* default settings after POR */
+       data->oversampling_setting = 0x00;
+
+       i2c_set_clientdata(client, data);
+
+       /* Initialize the BMP085 chip */
+       bmp085_init_client(client);
+
+       /* Register sysfs hooks */
+       err = sysfs_create_group(&client->dev.kobj, &bmp085_attr_group);
+       if (err)
+               goto exit_free;
+
+       printk(KERN_INFO "bmp085 probe succeeded!\n");
+       return 0;
+
+      exit_free:
+       kfree(data);
+      exit:
+       return err;
+}
+
+static int bmp085_remove(struct i2c_client *client)
+{
+       printk(KERN_INFO "bmp085 remove!\n");
+       sysfs_remove_group(&client->dev.kobj, &bmp085_attr_group);
+       kfree(i2c_get_clientdata(client));
+       return 0;
+}
+
+/* Called when we have found a new HMC5843. */
+static void bmp085_init_client(struct i2c_client *client)
+{
+       struct bmp085_data *data = i2c_get_clientdata(client);
+       bmp085_get_calibration_data(client);
+       data->version = i2c_smbus_read_byte_data(client, BMP085_VERSION_REG);
+       data->next_temp_measurement = 0;
+       data->oversampling_setting = 3;
+       printk(KERN_INFO "BMP085 ver. %d.%d initialized\n", (data->version & 
0x0F), (data->version & 0xF0) >> 4);
+}
+
+static const struct i2c_device_id bmp085_id[] = {
+       { "bmp085", 0 },
+       { }
+};
+
+static struct i2c_driver bmp085_driver = {
+       .driver = {
+               .name   = "bmp085",
+       },
+       .probe          = bmp085_probe,
+       .remove         = bmp085_remove,
+       .id_table       = bmp085_id,
+
+       .detect         = bmp085_detect,
+       .address_data   = &addr_data,
+};
+
+static int __init bmp085_init(void)
+{
+       printk(KERN_INFO "init!\n");
+       return i2c_add_driver(&bmp085_driver);
+}
+
+static void __exit bmp085_exit(void)
+{
+       printk(KERN_INFO "exit!\n");
+       i2c_del_driver(&bmp085_driver);
+}
+
+
+MODULE_AUTHOR("Christoph Mair <christoph.m...@gmail.com");
+MODULE_DESCRIPTION("BMP085 driver");
+MODULE_LICENSE("GPL");
+
+module_init(bmp085_init);
+module_exit(bmp085_exit);
-- 
1.7.0.5


Reply via email to