new file mode 100644
@@ -0,0 +1,371 @@
+/* Copyright (c) 2009 Christoph Mair <christoph.mair@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/i2c.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/input-polldev.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
+#define BMP085_CTRL_REG 0xF4
+#define BMP085_TEMP_REG 0x2E
+#define BMP085_PRESSURE_OSRS0 0x34
+#define BMP085_MSB 0xF6
+#define BMP085_LSB 0xF7
+#define BMP085_XLSB 0xF8
+#define BMP085_TEMP_CONV_TIME 5
+
+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 {
+ struct i2c_client *client;
+ struct mutex lock;
+ struct input_polled_dev *ipdev;
+ 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, BMP085_CTRL_REG,
+ BMP085_TEMP_REG);
+ if (status != 0) {
+ dev_err(&client->dev, "bmp085: Error requesting\
+ temperature measurement.\n");
+ return status;
+ }
+ msleep(BMP085_TEMP_CONV_TIME);
+
+ i2c_smbus_read_i2c_block_data(client, BMP085_MSB, 2, tmp);
+ /* next temperature measurement is needed in one second */
+ data->next_temp_measurement = jiffies + 1;
+ temperature = (tmp[0] << 8) + tmp[1];
+ pr_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, BMP085_CTRL_REG,
+ BMP085_PRESSURE_OSRS0 + (data->oversampling_setting<<6));
+ if (status != 0)
+ return status;
+
+ /* wait for the end of conversion */
+ msleep(2+(3 << data->oversampling_setting));
+
+ status = i2c_smbus_read_i2c_block_data(client, BMP085_MSB, 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);
+ 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 void bmp085_read_temperature(struct i2c_client *client, long *buf)
+{
+ 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;
+ *buf = ((x1+x2+8) >> 4) ;
+}
+
+static int bmp085_read_sensor(struct bmp085_data *bmp085,
+ long *pressure,
+ long *temp)
+{
+
+ struct i2c_client *client = bmp085->client;
+ 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;
+ long raw_temp;
+
+ bmp085_read_temperature(client, &raw_temp);
+ *temp = raw_temp ;
+ bmp085_read_pressure(client, &raw_pressure);
+
+ 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;
+
+ 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;
+
+ 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;
+ *pressure = p;
+ return 0;
+}
+
+static struct attribute *bmp085_attributes[] = {
+ &dev_attr_oversampling.attr,
+ NULL
+};
+
+static const struct attribute_group bmp085_attr_group = {
+ .attrs = bmp085_attributes,
+};
+
+static void bmp085_poll(struct input_polled_dev *ipdev)
+{
+ struct bmp085_data *bmp085 = ipdev->private;
+ long pressure_read, temperature;
+
+ mutex_lock(&bmp085->lock);
+ if (!bmp085_read_sensor(bmp085, &pressure_read, &temperature)) {
+ input_report_abs(ipdev->input, ABS_MISC, temperature);
+ input_report_abs(ipdev->input, ABS_PRESSURE, pressure_read);
+ input_sync(ipdev->input);
+ }
+ mutex_unlock(&bmp085->lock);
+}
+
+static int __devinit bmp085_input_init(struct bmp085_data *bmp085)
+{
+ int status;
+ struct input_polled_dev *ipdev;
+
+ ipdev = input_allocate_polled_device();
+ if (!ipdev) {
+ dev_dbg(&bmp085->client->dev, "error creating input device\n");
+ return -ENOMEM;
+ }
+ ipdev->poll = bmp085_poll;
+ ipdev->poll_interval = 1000; /* ms */
+ ipdev->private = bmp085;
+
+ ipdev->input->name = "Bosch BMP085 Pressure Sensor";
+ ipdev->input->phys = "bmp085/input0";
+ ipdev->input->id.bustype = BUS_HOST;
+
+ set_bit(EV_ABS, ipdev->input->evbit);
+
+ input_set_abs_params(ipdev->input, ABS_PRESSURE, 70000, 110000, 0, 0);
+ input_set_abs_params(ipdev->input, ABS_MISC, 0 , 650, 0, 0);
+
+ status = input_register_polled_device(ipdev);
+ if (status) {
+ dev_dbg(&bmp085->client->dev,
+ "error registering input device\n");
+ input_free_polled_device(ipdev);
+ goto exit;
+ }
+
+ bmp085->ipdev = ipdev;
+exit:
+ return status;
+}
+static int bmp085_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct bmp085_data *bmp085_data;
+ int err;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_dbg(&client->dev, "adapter doesn't support I2C\n");
+ return -ENODEV;
+ }
+
+ bmp085_data = kzalloc(sizeof(struct bmp085_data), GFP_KERNEL);
+ if (!bmp085_data) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ bmp085_data->client = client;
+
+ /* default settings after POR */
+ bmp085_data->oversampling_setting = 0x00;
+
+ i2c_set_clientdata(client, bmp085_data);
+
+ /* Initialize the BMP085 chip */
+ bmp085_init_client(client);
+
+ err = bmp085_input_init(bmp085_data);
+ if (err)
+ goto exit_free;
+
+ /* Register sysfs hooks */
+ err = sysfs_create_group(&client->dev.kobj, &bmp085_attr_group);
+ if (err)
+ dev_err(&client->dev,
+ "failed to create sysfs entries\n");
+
+ return 0;
+
+exit_free:
+ kfree(bmp085_data);
+exit:
+ return err;
+}
+
+static int bmp085_remove(struct i2c_client *client)
+{
+ struct bmp085_data *bmp085 = i2c_get_clientdata(client);
+
+ pr_info("bmp085 remove\n");
+ sysfs_remove_group(&client->dev.kobj, &bmp085_attr_group);
+ input_unregister_polled_device(bmp085->ipdev);
+ kfree(bmp085);
+ return 0;
+}
+
+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;
+ mutex_init(&data->lock);
+ pr_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",
+ .owner = THIS_MODULE,
+ },
+ .probe = bmp085_probe,
+ .remove = bmp085_remove,
+ .id_table = bmp085_id,
+};
+
+static int __init bmp085_init(void)
+{
+ return i2c_add_driver(&bmp085_driver);
+}
+
+static void __exit bmp085_exit(void)
+{
+ i2c_del_driver(&bmp085_driver);
+}
+
+MODULE_AUTHOR("Christoph Mair, Shubhrajyoti");
+MODULE_DESCRIPTION("BMP085 driver");
+MODULE_LICENSE("GPL");
+
+module_init(bmp085_init);
+module_exit(bmp085_exit);
+