Move files to create space for a userspace library.
[freerunner-navigation-board:bmp085.git] / kernel / bmp085.c
1 /*  Copyright (c) 2009  Christoph Mair <christoph.mair@gmail.com>
2
3     This program is free software; you can redistribute it and/or modify
4     it under the terms of the GNU General Public License as published by
5     the Free Software Foundation; either version 2 of the License, or
6     (at your option) any later version.
7
8     This program is distributed in the hope that it will be useful,
9     but WITHOUT ANY WARRANTY; without even the implied warranty of
10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11     GNU General Public License for more details.
12
13     You should have received a copy of the GNU General Public License
14     along with this program; if not, write to the Free Software
15     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 */
17
18
19 #include <linux/module.h>
20 #include <linux/init.h>
21 #include <linux/i2c.h>
22 #include <linux/delay.h>
23
24
25
26 #define BMP085_I2C_ADDRESS              0x77
27
28 #define BMP085_CALIBRATION_DATA_START   0xAA
29 #define BMP085_CALIBRATION_DATA_LENGTH  22
30
31 #define BMP085_CHIP_ID_REG              0xD0
32 #define BMP085_VERSION_REG              0xD1
33
34 #define BMP085_CHIP_ID                  0x55 /* 85 */
35
36
37 /* Addresses to scan: 0x77 */
38 static const unsigned short normal_i2c[] = { 0x77, I2C_CLIENT_END };
39
40 /* Insmod parameters */
41 I2C_CLIENT_INSMOD_1(bmp085);
42
43 struct bmp085_calibration_data {
44         s16 AC1, AC2, AC3;
45         u16 AC4, AC5, AC6;
46         s16 B1, B2;
47         s16 MB, MC, MD;
48 };
49
50
51 /* Each client has this additional data */
52 struct bmp085_data {
53         unsigned char version;
54         struct bmp085_calibration_data calibration;
55         unsigned char oversampling_setting;
56         unsigned long next_temp_measurement;
57         long b6; // calculated temperature correction coefficient
58 };
59
60 static void bmp085_init_client(struct i2c_client *client);
61
62
63 static s32 bmp085_get_calibration_data(struct i2c_client *client)
64 {
65         u8 tmp[BMP085_CALIBRATION_DATA_LENGTH];
66         struct bmp085_data *data = i2c_get_clientdata(client);
67         struct bmp085_calibration_data *cali = &(data->calibration);
68         s32 status = i2c_smbus_read_i2c_block_data(client, BMP085_CALIBRATION_DATA_START,
69                                                    BMP085_CALIBRATION_DATA_LENGTH, tmp);
70
71         cali->AC1 =  (tmp[0] <<8) | tmp[1];
72         cali->AC2 =  (tmp[2] <<8) | tmp[3];
73         cali->AC3 =  (tmp[4] <<8) | tmp[5];
74         cali->AC4 =  (tmp[6] <<8) | tmp[7];
75         cali->AC5 =  (tmp[8] <<8) | tmp[9];
76         cali->AC6 = (tmp[10] <<8) | tmp[11];
77
78         /*parameters B1,B2*/
79         cali->B1 =  (tmp[12] <<8) | tmp[13];
80         cali->B2 =  (tmp[14] <<8) | tmp[15];
81
82         /*parameters MB,MC,MD*/
83         cali->MB =  (tmp[16] <<8) | tmp[17];
84         cali->MC =  (tmp[18] <<8) | tmp[19];
85         cali->MD =  (tmp[20] <<8) | tmp[21];
86         return status;
87 }
88
89
90 static s32 bmp085_get_temperature(struct i2c_client *client)
91 {
92         struct bmp085_data *data = i2c_get_clientdata(client);
93         u16 temperature = 0x00;
94         u8 tmp[2];
95         s32 status = i2c_smbus_write_byte_data(client, 0xF4, 0x2E);
96         if (status != 0) {
97                 printk(KERN_INFO "bmp085: Error while requesting temperature measurement.\n");
98                 return status;
99         }
100         msleep(5);
101
102         i2c_smbus_read_i2c_block_data(client, 0xF6, 2, tmp);
103         data->next_temp_measurement = jiffies + 1;/**HZ;        /* next temperature measurement is needed in one second */
104         temperature = (tmp[0] << 8) + tmp[1];
105         printk(KERN_INFO "temperature: %u\n", temperature);
106         return temperature;
107 }
108
109
110 static s32 bmp085_read_pressure(struct i2c_client *client, u32 *pressure)
111 {
112         struct bmp085_data *data = i2c_get_clientdata(client);
113         u8 tmp[3];
114         s32 status = 0;
115         *pressure = 0;
116
117         status = i2c_smbus_write_byte_data(client, 0xF4, 0x34 + (data->oversampling_setting<<6));
118         if (status != 0) {
119                 return status;
120         }
121         msleep(2+(3 << data->oversampling_setting<<1)); /* wait for the end of conversion */
122
123         status = i2c_smbus_read_i2c_block_data(client, 0xF6, 0x03, tmp);
124         /* swap positions to correct the MSB/LSB positions*/
125         *pressure = (tmp[0] << 16) | (tmp[1] << 8) | tmp[2];
126         *pressure = *pressure >> (8-data->oversampling_setting);
127         printk(KERN_INFO "pressure: %u\n", *pressure);
128         return status;
129 }
130
131
132 /* sysfs callbacks */
133 static ssize_t set_oversampling(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
134 {
135         struct i2c_client *client = to_i2c_client(dev);
136         struct bmp085_data *data = i2c_get_clientdata(client);
137         data->oversampling_setting = simple_strtoul(buf, NULL, 10);
138         if (data->oversampling_setting > 3) {
139                 data->oversampling_setting = 3;
140         }
141         return count;
142 }
143
144 static ssize_t show_oversampling(struct device *dev, struct device_attribute *attr, char *buf)
145 {
146         struct i2c_client *client = to_i2c_client(dev);
147         struct bmp085_data *data = i2c_get_clientdata(client);
148         return sprintf(buf, "%u\n", data->oversampling_setting);
149 }
150 static DEVICE_ATTR(oversampling, S_IWUSR | S_IRUGO, show_oversampling, set_oversampling);
151
152
153 static ssize_t show_temperature(struct device *dev, struct device_attribute *attr, char *buf)
154 {
155         struct i2c_client *client = to_i2c_client(dev);
156         struct bmp085_data *data = i2c_get_clientdata(client);
157         struct bmp085_calibration_data *cali = &data->calibration;
158
159         long raw_temp = bmp085_get_temperature(client);
160         long x1 = ((raw_temp - cali->AC6) * cali->AC5) >> 15;
161         long x2 = (cali->MC << 11) / (x1 + cali->MD);
162         data->b6 = x1 + x2 - 4000;
163 //      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);
164         return sprintf(buf, "%ld\n", (x1+x2+8) >> 4);
165 }
166 static DEVICE_ATTR(temperature, S_IRUGO, show_temperature, NULL);
167
168
169 static ssize_t show_pressure(struct device *dev, struct device_attribute *attr, char *buf)
170 {
171         struct i2c_client *client = to_i2c_client(dev);
172         struct bmp085_data *data = i2c_get_clientdata(client);
173         struct bmp085_calibration_data *cali = &data->calibration;
174         long x1, x2, x3, b3;
175         unsigned long b4, b7;
176         long p;
177         unsigned int raw_pressure;
178         if (time_after(jiffies, data->next_temp_measurement)) {
179                 printk(KERN_INFO "new temp required: %lu v.s. %lu\n", jiffies, data->next_temp_measurement);
180                 show_temperature(dev, attr, buf); /* read temperature and calculate coefficients */
181         }
182
183         bmp085_read_pressure(client, &raw_pressure);
184 //      printk(KERN_INFO "raw_pressure: %ud, B6: %ld\n", raw_pressure, data->b6);
185
186         x1 = (data->b6 * data->b6) >> 12;
187         x1 *= cali->B2;
188         x1 >>= 11;
189
190         x2 = cali->AC2 * data->b6;
191         x2 >>= 11;
192
193         x3 = x1 + x2;
194
195         b3 = (((((long)cali->AC1) * 4 + x3) << data->oversampling_setting) + 2) >> 2;
196
197 //      printk(KERN_INFO "x1: %ld, x2: %ld, x3: %ld, b3: %ld\n", x1, x2, x3, b3);
198         x1 = (cali->AC3 * data->b6) >> 13;
199         x2 = (cali->B1 * ((data->b6 * data->b6) >> 12)) >> 16;
200         x3 = (x1 + x2 + 2) >> 2;
201         b4 = (cali->AC4 * (unsigned long)(x3 + 32768)) >> 15;
202
203 //      printk(KERN_INFO "x1: %ld, x2: %ld, x3: %ld, b4: %ld\n", x1, x2, x3, b4);
204         b7 = ((unsigned long)raw_pressure - b3) * (50000 >> data->oversampling_setting);
205         p = ((b7 < 0x80000000) ? ((b7 << 1) / b4) : ((b7 / b4) * 2));
206
207         x1 = p >> 8;
208         x1 *= x1;
209         x1 = (x1 * 3038) >> 16;
210         x2 = (-7357 * p) >> 16;
211         p += (x1 + x2 + 3791) >> 4;
212         return sprintf(buf, "%ld\n", p);
213 }
214 static DEVICE_ATTR(pressure, S_IRUGO, show_pressure, NULL);
215
216
217 static struct attribute *bmp085_attributes[] = {
218         &dev_attr_temperature.attr,
219         &dev_attr_pressure.attr,
220         &dev_attr_oversampling.attr,
221         NULL
222 };
223
224 static const struct attribute_group bmp085_attr_group = {
225         .attrs = bmp085_attributes,
226 };
227
228
229 /* Return 0 if detection is successful, -ENODEV otherwise */
230 static int bmp085_detect(struct i2c_client *client, int kind, struct i2c_board_info *info)
231 {
232         struct i2c_adapter *adapter = client->adapter;
233         const char *client_name;
234
235         if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
236                 return -ENODEV;
237
238         if (client->addr == BMP085_I2C_ADDRESS) {
239                 if (i2c_smbus_read_byte_data(client, BMP085_CHIP_ID_REG) != BMP085_CHIP_ID) {
240                         goto exit;
241                 }
242
243                 /* Chip identified */
244                 client_name = "bmp085";
245                 strlcpy(info->type, client_name, I2C_NAME_SIZE);
246                 printk(KERN_INFO "BMP085 found!\n");
247                 return 0;
248         }
249
250       exit:
251         printk(KERN_INFO "BMP085 not found at address %x!\n", client->addr);
252         return -ENODEV;
253 }
254
255 static int bmp085_probe(struct i2c_client *client,
256                          const struct i2c_device_id *id)
257 {
258         struct bmp085_data *data;
259         int err;
260
261         printk(KERN_INFO "bmp085 probe!\n");
262
263         data = kzalloc(sizeof(struct bmp085_data), GFP_KERNEL);
264         if (!data) {
265                 err = -ENOMEM;
266                 goto exit;
267         }
268
269         /* default settings after POR */
270         data->oversampling_setting = 0x00;
271
272         i2c_set_clientdata(client, data);
273
274         /* Initialize the BMP085 chip */
275         bmp085_init_client(client);
276
277         /* Register sysfs hooks */
278         err = sysfs_create_group(&client->dev.kobj, &bmp085_attr_group);
279         if (err)
280                 goto exit_free;
281
282         printk(KERN_INFO "bmp085 probe succeeded!\n");
283         return 0;
284
285       exit_free:
286         kfree(data);
287       exit:
288         return err;
289 }
290
291 static int bmp085_remove(struct i2c_client *client)
292 {
293         printk(KERN_INFO "bmp085 remove!\n");
294         sysfs_remove_group(&client->dev.kobj, &bmp085_attr_group);
295         kfree(i2c_get_clientdata(client));
296         return 0;
297 }
298
299 /* Called when we have found a new HMC5843. */
300 static void bmp085_init_client(struct i2c_client *client)
301 {
302         struct bmp085_data *data = i2c_get_clientdata(client);
303         bmp085_get_calibration_data(client);
304         data->version = i2c_smbus_read_byte_data(client, BMP085_VERSION_REG);
305         data->next_temp_measurement = 0;
306         data->oversampling_setting = 3;
307         printk(KERN_INFO "BMP085 ver. %d.%d initialized\n", (data->version & 0x0F), (data->version & 0xF0) >> 4);
308 }
309
310 static const struct i2c_device_id bmp085_id[] = {
311         { "bmp085", 0 },
312         { }
313 };
314
315 static struct i2c_driver bmp085_driver = {
316         .driver = {
317                 .name   = "bmp085",
318         },
319         .probe          = bmp085_probe,
320         .remove         = bmp085_remove,
321         .id_table       = bmp085_id,
322
323         .detect         = bmp085_detect,
324         .address_data   = &addr_data,
325 };
326
327 static int __init bmp085_init(void)
328 {
329         printk(KERN_INFO "init!\n");
330         return i2c_add_driver(&bmp085_driver);
331 }
332
333 static void __exit bmp085_exit(void)
334 {
335         printk(KERN_INFO "exit!\n");
336         i2c_del_driver(&bmp085_driver);
337 }
338
339
340 MODULE_AUTHOR("Christoph Mair <christoph.mair@gmail.com");
341 MODULE_DESCRIPTION("BMP085 driver");
342 MODULE_LICENSE("GPL");
343
344 module_init(bmp085_init);
345 module_exit(bmp085_exit);