Refactor driver. Some code cleanups.
[freerunner-navigation-board:bmp085.git] / kernel / bmp085.c
1 /*  Copyright (c) 2010  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[] = { BMP085_I2C_ADDRESS,
39                                                         I2C_CLIENT_END };
40
41 /* Insmod parameters */
42 I2C_CLIENT_INSMOD_1(bmp085);
43
44 struct bmp085_calibration_data {
45         s16 AC1, AC2, AC3;
46         u16 AC4, AC5, AC6;
47         s16 B1, B2;
48         s16 MB, MC, MD;
49 };
50
51
52 /* Each client has this additional data */
53 struct bmp085_data {
54         struct i2c_client *client;
55         unsigned char version;
56         struct bmp085_calibration_data calibration;
57         unsigned long raw_temperature;
58         unsigned long raw_pressure;
59         unsigned char oversampling_setting;
60         unsigned long next_temp_measurement;
61         long b6; /* calculated temperature correction coefficient */
62 };
63
64
65 static void bmp085_init_client(struct i2c_client *client);
66
67
68 static s32 bmp085_read_calibration_data(struct i2c_client *client)
69 {
70         u8 tmp[BMP085_CALIBRATION_DATA_LENGTH];
71         struct bmp085_data *data = i2c_get_clientdata(client);
72         struct bmp085_calibration_data *cali = &(data->calibration);
73         s32 status = i2c_smbus_read_i2c_block_data(client,
74                                         BMP085_CALIBRATION_DATA_START,
75                                         BMP085_CALIBRATION_DATA_LENGTH, tmp);
76
77         cali->AC1 =  (tmp[0] << 8) | tmp[1];
78         cali->AC2 =  (tmp[2] << 8) | tmp[3];
79         cali->AC3 =  (tmp[4] << 8) | tmp[5];
80         cali->AC4 =  (tmp[6] << 8) | tmp[7];
81         cali->AC5 =  (tmp[8] << 8) | tmp[9];
82         cali->AC6 = (tmp[10] << 8) | tmp[11];
83         cali->B1 =  (tmp[12] << 8) | tmp[13];
84         cali->B2 =  (tmp[14] << 8) | tmp[15];
85         cali->MB =  (tmp[16] << 8) | tmp[17];
86         cali->MC =  (tmp[18] << 8) | tmp[19];
87         cali->MD =  (tmp[20] << 8) | tmp[21];
88         return status;
89 }
90
91
92 static s32 bmp085_update_raw_temperature(struct bmp085_data *data)
93 {
94         u8 tmp[2];
95         s32 status = i2c_smbus_write_byte_data(data->client, 0xF4, 0x2E);
96         if (status != 0) {
97                 printk(KERN_INFO "bmp085: Error while requesting"
98                                                 " temperature measurement.\n");
99                 return status;
100         }
101         msleep(5);
102
103         status = i2c_smbus_read_i2c_block_data(data->client, 0xF6, sizeof(tmp), tmp);
104         if (status != sizeof(tmp)) {
105                 printk(KERN_INFO "bmp085: Error while requesting temperature"
106                                         " measurement (II): %d\n", status);
107                 return status;
108         }
109         data->raw_temperature = (tmp[0] << 8) + tmp[1];
110         data->next_temp_measurement = jiffies+1*HZ;
111         return status;
112 }
113
114 static s32 bmp085_update_raw_pressure(struct bmp085_data *data)
115 {
116         u8 tmp[3];
117         s32 status = 0;
118
119         status = i2c_smbus_write_byte_data(data->client, 0xF4, 0x34 + (data->oversampling_setting<<6));
120         if (status != 0) {
121                 printk(KERN_INFO "bmp085: Error while requesting pressure measurement.\n");
122                 return status;
123         }
124         msleep(2+(3 << data->oversampling_setting<<1)); /* wait for the end of conversion */
125
126         status = i2c_smbus_read_i2c_block_data(data->client, 0xF6, sizeof(tmp), tmp);
127         if (status != sizeof(tmp)) {
128                 printk(KERN_INFO "bmp085: Error while requesting pressure measurement (II): %d\n", status);
129                 return status;
130         }
131         /* swap positions to correct the MSB/LSB positions */
132         data->raw_pressure = (tmp[0] << 16) | (tmp[1] << 8) | tmp[2];
133         data->raw_pressure = data->raw_pressure >> (8-data->oversampling_setting);
134         return status;
135 }
136
137
138 static s32 bmp085_get_temperature(struct bmp085_data *data)
139 {
140         struct bmp085_calibration_data *cali = &data->calibration;
141         long x1, x2;
142
143         if (data->next_temp_measurement < jiffies) {
144                 bmp085_update_raw_temperature(data);
145         }
146
147         x1 = ((data->raw_temperature - cali->AC6) * cali->AC5) >> 15;
148         x2 = (cali->MC << 11) / (x1 + cali->MD);
149         data->b6 = x1 + x2 - 4000;
150 //      printf("got raw temp: %ld; AC5: %d, AC6: %d, MC: %d, MD: %d\n", data->raw_temperature, cali->AC5, cali->AC6, cali->MC, cali->MD);
151         return ((x1+x2+8) >> 4);
152 }
153
154
155 static s32 bmp085_get_pressure(struct bmp085_data *data)
156 {
157         struct bmp085_calibration_data *cali = &data->calibration;
158         long x1, x2, x3, b3;
159         unsigned long b4, b7;
160         long p;
161
162         if (data->next_temp_measurement < jiffies) {
163                 bmp085_get_temperature(data);
164         }
165         bmp085_update_raw_pressure(data);
166
167 //      printf("raw_pressure: %ud, B6: %ld\n", data->raw_pressure, data->b6);
168
169         x1 = (data->b6 * data->b6) >> 12;
170         x1 *= cali->B2;
171         x1 >>= 11;
172
173         x2 = cali->AC2 * data->b6;
174         x2 >>= 11;
175
176         x3 = x1 + x2;
177
178         b3 = (((((long)cali->AC1) * 4 + x3) << data->oversampling_setting) + 2) >> 2;
179
180 //      printf("x1: %ld, x2: %ld, x3: %ld, b3: %ld\n", x1, x2, x3, b3);
181         x1 = (cali->AC3 * data->b6) >> 13;
182         x2 = (cali->B1 * ((data->b6 * data->b6) >> 12)) >> 16;
183         x3 = (x1 + x2 + 2) >> 2;
184         b4 = (cali->AC4 * (unsigned long)(x3 + 32768)) >> 15;
185
186 //      printf("x1: %ld, x2: %ld, x3: %ld, b4: %ld\n", x1, x2, x3, b4);
187         b7 = ((unsigned long)data->raw_pressure - b3) * (50000 >> data->oversampling_setting);
188         p = ((b7 < 0x80000000) ? ((b7 << 1) / b4) : ((b7 / b4) * 2));
189
190         x1 = p >> 8;
191         x1 *= x1;
192         x1 = (x1 * 3038) >> 16;
193         x2 = (-7357 * p) >> 16;
194         p += (x1 + x2 + 3791) >> 4;
195         return p;
196 }
197
198
199 static void bmp085_set_oversampling(struct bmp085_data *data, unsigned char oversampling)
200 {
201         if (oversampling> 3) {
202                 oversampling = 3;
203         }
204         data->oversampling_setting = oversampling;
205 }
206
207
208 static unsigned char bmp085_get_oversampling(struct bmp085_data *data)
209 {
210         return data->oversampling_setting;
211 }
212
213
214
215
216
217
218
219 /* sysfs callbacks */
220 static ssize_t set_oversampling(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
221 {
222         struct i2c_client *client = to_i2c_client(dev);
223         struct bmp085_data *data = i2c_get_clientdata(client);
224         bmp085_set_oversampling(data, simple_strtoul(buf, NULL, 10));
225         return count;
226 }
227
228 static ssize_t show_oversampling(struct device *dev, struct device_attribute *attr, char *buf)
229 {
230         struct i2c_client *client = to_i2c_client(dev);
231         struct bmp085_data *data = i2c_get_clientdata(client);
232         return sprintf(buf, "%u\n", bmp085_get_oversampling(data));
233 }
234 static DEVICE_ATTR(oversampling, S_IWUSR | S_IRUGO, show_oversampling, set_oversampling);
235
236
237 static ssize_t show_temperature(struct device *dev, struct device_attribute *attr, char *buf)
238 {
239         struct i2c_client *client = to_i2c_client(dev);
240         struct bmp085_data *data = i2c_get_clientdata(client);
241         return sprintf(buf, "%d\n", bmp085_get_temperature(data));
242 }
243 static DEVICE_ATTR(temperature, S_IRUGO, show_temperature, NULL);
244
245
246 static ssize_t show_pressure(struct device *dev, struct device_attribute *attr, char *buf)
247 {
248         struct i2c_client *client = to_i2c_client(dev);
249         struct bmp085_data *data = i2c_get_clientdata(client);
250         return sprintf(buf, "%d\n", bmp085_get_pressure(data));
251 }
252 static DEVICE_ATTR(pressure, S_IRUGO, show_pressure, NULL);
253
254
255 static struct attribute *bmp085_attributes[] = {
256         &dev_attr_temperature.attr,
257         &dev_attr_pressure.attr,
258         &dev_attr_oversampling.attr,
259         NULL
260 };
261
262 static const struct attribute_group bmp085_attr_group = {
263         .attrs = bmp085_attributes,
264 };
265
266
267 /* Return 0 if detection is successful, -ENODEV otherwise */
268 static int bmp085_detect(struct i2c_client *client, int kind, struct i2c_board_info *info)
269 {
270         struct i2c_adapter *adapter = client->adapter;
271         const char *client_name;
272
273         if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
274                 return -ENODEV;
275
276         if (client->addr == BMP085_I2C_ADDRESS) {
277                 if (i2c_smbus_read_byte_data(client, BMP085_CHIP_ID_REG) != BMP085_CHIP_ID) {
278                         goto exit;
279                 }
280
281                 /* Chip identified */
282                 client_name = "bmp085";
283                 strlcpy(info->type, client_name, I2C_NAME_SIZE);
284                 printk(KERN_INFO "BMP085 found!\n");
285                 return 0;
286         }
287
288       exit:
289         printk(KERN_INFO "BMP085 not found at address %x!\n", client->addr);
290         return -ENODEV;
291 }
292
293 static int bmp085_probe(struct i2c_client *client,
294                          const struct i2c_device_id *id)
295 {
296         struct bmp085_data *data;
297         int err;
298
299         printk(KERN_INFO "bmp085 probe!\n");
300
301         data = kzalloc(sizeof(struct bmp085_data), GFP_KERNEL);
302         if (!data) {
303                 err = -ENOMEM;
304                 goto exit;
305         }
306
307         /* default settings after POR */
308         data->oversampling_setting = 0x00;
309
310         i2c_set_clientdata(client, data);
311
312         /* Initialize the BMP085 chip */
313         bmp085_init_client(client);
314
315         /* Register sysfs hooks */
316         err = sysfs_create_group(&client->dev.kobj, &bmp085_attr_group);
317         if (err)
318                 goto exit_free;
319
320         printk(KERN_INFO "bmp085 probe succeeded!\n");
321         return 0;
322
323       exit_free:
324         kfree(data);
325       exit:
326         return err;
327 }
328
329 static int bmp085_remove(struct i2c_client *client)
330 {
331         printk(KERN_INFO "bmp085 remove!\n");
332         sysfs_remove_group(&client->dev.kobj, &bmp085_attr_group);
333         kfree(i2c_get_clientdata(client));
334         return 0;
335 }
336
337 /* Called when we have found a new HMC5843. */
338 static void bmp085_init_client(struct i2c_client *client)
339 {
340         struct bmp085_data *data = i2c_get_clientdata(client);
341         bmp085_read_calibration_data(client);
342         data->version = i2c_smbus_read_byte_data(client, BMP085_VERSION_REG);
343         data->next_temp_measurement = 0;
344         data->oversampling_setting = 3;
345         printk(KERN_INFO "BMP085 ver. %d.%d initialized\n", (data->version & 0x0F), (data->version & 0xF0) >> 4);
346 }
347
348 static const struct i2c_device_id bmp085_id[] = {
349         { "bmp085", 0 },
350         { }
351 };
352
353 static struct i2c_driver bmp085_driver = {
354         .driver = {
355                 .name   = "bmp085",
356         },
357         .probe          = bmp085_probe,
358         .remove         = bmp085_remove,
359         .id_table       = bmp085_id,
360
361         .detect         = bmp085_detect,
362         .address_data   = &addr_data,
363 };
364
365 static int __init bmp085_init(void)
366 {
367         printk(KERN_INFO "init!\n");
368         return i2c_add_driver(&bmp085_driver);
369 }
370
371 static void __exit bmp085_exit(void)
372 {
373         printk(KERN_INFO "exit!\n");
374         i2c_del_driver(&bmp085_driver);
375 }
376
377
378 MODULE_AUTHOR("Christoph Mair <christoph.mair@gmail.com");
379 MODULE_DESCRIPTION("BMP085 driver");
380 MODULE_LICENSE("GPL");
381
382 module_init(bmp085_init);
383 module_exit(bmp085_exit);