hongji.zhou@cn.bosch.com | f1204ad | 2013-02-05 14:45:04 +0800 | [diff] [blame] | 1 | /* Copyright (c) 2011 Bosch Sensortec GmbH |
| 2 | Copyright (c) 2011 Unixphere |
| 3 | |
| 4 | Based on: |
| 5 | BMP085 driver, bmp085.c |
| 6 | Copyright (c) 2010 Christoph Mair <christoph.mair@gmail.com> |
| 7 | |
| 8 | This driver supports the bmp18x digital barometric pressure |
| 9 | and temperature sensors from Bosch Sensortec. |
| 10 | |
| 11 | A pressure measurement is issued by reading from pressure0_input. |
| 12 | The return value ranges from 30000 to 110000 pascal with a resulution |
| 13 | of 1 pascal (0.01 millibar) which enables measurements from 9000m above |
| 14 | to 500m below sea level. |
| 15 | |
| 16 | The temperature can be read from temp0_input. Values range from |
| 17 | -400 to 850 representing the ambient temperature in degree celsius |
| 18 | multiplied by 10.The resolution is 0.1 celsius. |
| 19 | |
| 20 | Because ambient pressure is temperature dependent, a temperature |
| 21 | measurement will be executed automatically even if the user is reading |
| 22 | from pressure0_input. This happens if the last temperature measurement |
| 23 | has been executed more then one second ago. |
| 24 | |
| 25 | To decrease RMS noise from pressure measurements, the bmp18x can |
| 26 | autonomously calculate the average of up to eight samples. This is |
| 27 | set up by writing to the oversampling sysfs file. Accepted values |
| 28 | are 0, 1, 2 and 3. 2^x when x is the value written to this file |
| 29 | specifies the number of samples used to calculate the ambient pressure. |
| 30 | RMS noise is specified with six pascal (without averaging) and decreases |
| 31 | down to 3 pascal when using an oversampling setting of 3. |
| 32 | |
| 33 | This program is free software; you can redistribute it and/or modify |
| 34 | it under the terms of the GNU General Public License as published by |
| 35 | the Free Software Foundation; either version 2 of the License, or |
| 36 | (at your option) any later version. |
| 37 | |
| 38 | This program is distributed in the hope that it will be useful, |
| 39 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 40 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 41 | GNU General Public License for more details. |
| 42 | |
| 43 | You should have received a copy of the GNU General Public License |
| 44 | along with this program; if not, write to the Free Software |
| 45 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| 46 | */ |
| 47 | |
| 48 | #include <linux/device.h> |
| 49 | #include <linux/init.h> |
| 50 | #include <linux/slab.h> |
| 51 | #include <linux/delay.h> |
| 52 | #include <linux/input.h> |
| 53 | #include <linux/workqueue.h> |
| 54 | #include <linux/module.h> |
| 55 | #ifdef CONFIG_HAS_EARLYSUSPEND |
| 56 | #include <linux/earlysuspend.h> |
| 57 | #endif |
| 58 | #include "bmp18x.h" |
| 59 | |
| 60 | #define BMP18X_CHIP_ID 0x55 |
| 61 | |
| 62 | #define BMP18X_CALIBRATION_DATA_START 0xAA |
| 63 | #define BMP18X_CALIBRATION_DATA_LENGTH 11 /* 16 bit values */ |
| 64 | #define BMP18X_CHIP_ID_REG 0xD0 |
| 65 | #define BMP18X_CTRL_REG 0xF4 |
| 66 | #define BMP18X_TEMP_MEASUREMENT 0x2E |
| 67 | #define BMP18X_PRESSURE_MEASUREMENT 0x34 |
| 68 | #define BMP18X_CONVERSION_REGISTER_MSB 0xF6 |
| 69 | #define BMP18X_CONVERSION_REGISTER_LSB 0xF7 |
| 70 | #define BMP18X_CONVERSION_REGISTER_XLSB 0xF8 |
| 71 | #define BMP18X_TEMP_CONVERSION_TIME 5 |
| 72 | |
| 73 | #define ABS_MIN_PRESSURE 30000 |
| 74 | #define ABS_MAX_PRESSURE 120000 |
| 75 | #define BMP_DELAY_DEFAULT 200 |
| 76 | |
| 77 | struct bmp18x_calibration_data { |
| 78 | s16 AC1, AC2, AC3; |
| 79 | u16 AC4, AC5, AC6; |
| 80 | s16 B1, B2; |
| 81 | s16 MB, MC, MD; |
| 82 | }; |
| 83 | |
| 84 | /* Each client has this additional data */ |
| 85 | struct bmp18x_data { |
| 86 | struct bmp18x_data_bus data_bus; |
| 87 | struct device *dev; |
| 88 | struct mutex lock; |
| 89 | struct bmp18x_calibration_data calibration; |
| 90 | u8 oversampling_setting; |
| 91 | u8 sw_oversampling_setting; |
| 92 | u32 raw_temperature; |
| 93 | u32 raw_pressure; |
| 94 | u32 temp_measurement_period; |
| 95 | u32 last_temp_measurement; |
| 96 | s32 b6; /* calculated temperature correction coefficient */ |
| 97 | #ifdef CONFIG_HAS_EARLYSUSPEND |
| 98 | struct early_suspend early_suspend; |
| 99 | #endif |
| 100 | struct input_dev *input; |
| 101 | struct delayed_work work; |
| 102 | u32 delay; |
| 103 | u32 enable; |
| 104 | }; |
| 105 | |
| 106 | #ifdef CONFIG_HAS_EARLYSUSPEND |
| 107 | static void bmp18x_early_suspend(struct early_suspend *h); |
| 108 | static void bmp18x_late_resume(struct early_suspend *h); |
| 109 | #endif |
| 110 | |
| 111 | static s32 bmp18x_read_calibration_data(struct bmp18x_data *data) |
| 112 | { |
| 113 | u16 tmp[BMP18X_CALIBRATION_DATA_LENGTH]; |
| 114 | struct bmp18x_calibration_data *cali = &(data->calibration); |
| 115 | s32 status = data->data_bus.bops->read_block(data->data_bus.client, |
| 116 | BMP18X_CALIBRATION_DATA_START, |
| 117 | BMP18X_CALIBRATION_DATA_LENGTH*sizeof(u16), |
| 118 | (u8 *)tmp); |
| 119 | if (status < 0) |
| 120 | return status; |
| 121 | |
| 122 | if (status != BMP18X_CALIBRATION_DATA_LENGTH*sizeof(u16)) |
| 123 | return -EIO; |
| 124 | |
| 125 | cali->AC1 = be16_to_cpu(tmp[0]); |
| 126 | cali->AC2 = be16_to_cpu(tmp[1]); |
| 127 | cali->AC3 = be16_to_cpu(tmp[2]); |
| 128 | cali->AC4 = be16_to_cpu(tmp[3]); |
| 129 | cali->AC5 = be16_to_cpu(tmp[4]); |
| 130 | cali->AC6 = be16_to_cpu(tmp[5]); |
| 131 | cali->B1 = be16_to_cpu(tmp[6]); |
| 132 | cali->B2 = be16_to_cpu(tmp[7]); |
| 133 | cali->MB = be16_to_cpu(tmp[8]); |
| 134 | cali->MC = be16_to_cpu(tmp[9]); |
| 135 | cali->MD = be16_to_cpu(tmp[10]); |
| 136 | return 0; |
| 137 | } |
| 138 | |
| 139 | |
| 140 | static s32 bmp18x_update_raw_temperature(struct bmp18x_data *data) |
| 141 | { |
| 142 | u16 tmp; |
| 143 | s32 status; |
| 144 | |
| 145 | mutex_lock(&data->lock); |
| 146 | status = data->data_bus.bops->write_byte(data->data_bus.client, |
| 147 | BMP18X_CTRL_REG, BMP18X_TEMP_MEASUREMENT); |
| 148 | if (status != 0) { |
| 149 | dev_err(data->dev, |
| 150 | "Error while requesting temperature measurement.\n"); |
| 151 | goto exit; |
| 152 | } |
| 153 | msleep(BMP18X_TEMP_CONVERSION_TIME); |
| 154 | |
| 155 | status = data->data_bus.bops->read_block(data->data_bus.client, |
| 156 | BMP18X_CONVERSION_REGISTER_MSB, sizeof(tmp), (u8 *)&tmp); |
| 157 | if (status < 0) |
| 158 | goto exit; |
| 159 | if (status != sizeof(tmp)) { |
| 160 | dev_err(data->dev, |
| 161 | "Error while reading temperature measurement result\n"); |
| 162 | status = -EIO; |
| 163 | goto exit; |
| 164 | } |
| 165 | data->raw_temperature = be16_to_cpu(tmp); |
| 166 | data->last_temp_measurement = jiffies; |
| 167 | status = 0; /* everything ok, return 0 */ |
| 168 | |
| 169 | exit: |
| 170 | mutex_unlock(&data->lock); |
| 171 | return status; |
| 172 | } |
| 173 | |
| 174 | static s32 bmp18x_update_raw_pressure(struct bmp18x_data *data) |
| 175 | { |
| 176 | u32 tmp = 0; |
| 177 | s32 status; |
| 178 | |
| 179 | mutex_lock(&data->lock); |
| 180 | status = data->data_bus.bops->write_byte(data->data_bus.client, |
| 181 | BMP18X_CTRL_REG, BMP18X_PRESSURE_MEASUREMENT + |
| 182 | (data->oversampling_setting<<6)); |
| 183 | if (status != 0) { |
| 184 | dev_err(data->dev, |
| 185 | "Error while requesting pressure measurement.\n"); |
| 186 | goto exit; |
| 187 | } |
| 188 | |
| 189 | /* wait for the end of conversion */ |
| 190 | msleep(2+(3 << data->oversampling_setting)); |
| 191 | |
| 192 | /* copy data into a u32 (4 bytes), but skip the first byte. */ |
| 193 | status = data->data_bus.bops->read_block(data->data_bus.client, |
| 194 | BMP18X_CONVERSION_REGISTER_MSB, 3, ((u8 *)&tmp)+1); |
| 195 | if (status < 0) |
| 196 | goto exit; |
| 197 | if (status != 3) { |
| 198 | dev_err(data->dev, |
| 199 | "Error while reading pressure measurement results\n"); |
| 200 | status = -EIO; |
| 201 | goto exit; |
| 202 | } |
| 203 | data->raw_pressure = be32_to_cpu((tmp)); |
| 204 | data->raw_pressure >>= (8-data->oversampling_setting); |
| 205 | status = 0; /* everything ok, return 0 */ |
| 206 | |
| 207 | exit: |
| 208 | mutex_unlock(&data->lock); |
| 209 | return status; |
| 210 | } |
| 211 | |
| 212 | |
| 213 | /* |
| 214 | * This function starts the temperature measurement and returns the value |
| 215 | * in tenth of a degree celsius. |
| 216 | */ |
| 217 | static s32 bmp18x_get_temperature(struct bmp18x_data *data, int *temperature) |
| 218 | { |
| 219 | struct bmp18x_calibration_data *cali = &data->calibration; |
| 220 | long x1, x2; |
| 221 | int status; |
| 222 | |
| 223 | status = bmp18x_update_raw_temperature(data); |
| 224 | if (status != 0) |
| 225 | goto exit; |
| 226 | |
| 227 | x1 = ((data->raw_temperature - cali->AC6) * cali->AC5) >> 15; |
| 228 | x2 = (cali->MC << 11) / (x1 + cali->MD); |
| 229 | data->b6 = x1 + x2 - 4000; |
| 230 | /* if NULL just update b6. Used for pressure only measurements */ |
| 231 | if (temperature != NULL) |
| 232 | *temperature = (x1+x2+8) >> 4; |
| 233 | |
| 234 | exit: |
| 235 | return status; |
| 236 | } |
| 237 | |
| 238 | /* |
| 239 | * This function starts the pressure measurement and returns the value |
| 240 | * in millibar. Since the pressure depends on the ambient temperature, |
| 241 | * a temperature measurement is executed according to the given temperature |
| 242 | * measurememt period (default is 1 sec boundary). This period could vary |
| 243 | * and needs to be adjusted accoring to the sensor environment, i.e. if big |
| 244 | * temperature variations then the temperature needs to be read out often. |
| 245 | */ |
| 246 | static s32 bmp18x_get_pressure(struct bmp18x_data *data, int *pressure) |
| 247 | { |
| 248 | struct bmp18x_calibration_data *cali = &data->calibration; |
| 249 | s32 x1, x2, x3, b3; |
| 250 | u32 b4, b7; |
| 251 | s32 p; |
| 252 | int status; |
| 253 | int i_loop, i; |
| 254 | u32 p_tmp; |
| 255 | |
| 256 | /* update the ambient temperature according to the given meas. period */ |
| 257 | if (data->last_temp_measurement + |
| 258 | data->temp_measurement_period < jiffies) { |
| 259 | status = bmp18x_get_temperature(data, NULL); |
| 260 | if (status != 0) |
| 261 | goto exit; |
| 262 | } |
| 263 | |
| 264 | if ((data->oversampling_setting == 3) |
| 265 | && (data->sw_oversampling_setting == 1)) { |
| 266 | i_loop = 3; |
| 267 | } else { |
| 268 | i_loop = 1; |
| 269 | } |
| 270 | |
| 271 | p_tmp = 0; |
| 272 | for (i = 0; i < i_loop; i++) { |
| 273 | status = bmp18x_update_raw_pressure(data); |
| 274 | if (status != 0) |
| 275 | goto exit; |
| 276 | p_tmp += data->raw_pressure; |
| 277 | } |
| 278 | |
| 279 | data->raw_pressure = (p_tmp + (i_loop >> 1)) / i_loop; |
| 280 | |
| 281 | x1 = (data->b6 * data->b6) >> 12; |
| 282 | x1 *= cali->B2; |
| 283 | x1 >>= 11; |
| 284 | |
| 285 | x2 = cali->AC2 * data->b6; |
| 286 | x2 >>= 11; |
| 287 | |
| 288 | x3 = x1 + x2; |
| 289 | |
| 290 | b3 = (((((s32)cali->AC1) * 4 + x3) << data->oversampling_setting) + 2); |
| 291 | b3 >>= 2; |
| 292 | |
| 293 | x1 = (cali->AC3 * data->b6) >> 13; |
| 294 | x2 = (cali->B1 * ((data->b6 * data->b6) >> 12)) >> 16; |
| 295 | x3 = (x1 + x2 + 2) >> 2; |
| 296 | b4 = (cali->AC4 * (u32)(x3 + 32768)) >> 15; |
| 297 | |
| 298 | b7 = ((u32)data->raw_pressure - b3) * |
| 299 | (50000 >> data->oversampling_setting); |
| 300 | p = ((b7 < 0x80000000) ? ((b7 << 1) / b4) : ((b7 / b4) * 2)); |
| 301 | |
| 302 | x1 = p >> 8; |
| 303 | x1 *= x1; |
| 304 | x1 = (x1 * 3038) >> 16; |
| 305 | x2 = (-7357 * p) >> 16; |
| 306 | p += (x1 + x2 + 3791) >> 4; |
| 307 | |
| 308 | *pressure = p; |
| 309 | |
| 310 | exit: |
| 311 | return status; |
| 312 | } |
| 313 | |
| 314 | /* |
| 315 | * This function sets the chip-internal oversampling. Valid values are 0..3. |
| 316 | * The chip will use 2^oversampling samples for internal averaging. |
| 317 | * This influences the measurement time and the accuracy; larger values |
| 318 | * increase both. The datasheet gives on overview on how measurement time, |
| 319 | * accuracy and noise correlate. |
| 320 | */ |
| 321 | static void bmp18x_set_oversampling(struct bmp18x_data *data, |
| 322 | unsigned char oversampling) |
| 323 | { |
| 324 | if (oversampling > 3) |
| 325 | oversampling = 3; |
| 326 | data->oversampling_setting = oversampling; |
| 327 | } |
| 328 | |
| 329 | /* |
| 330 | * Returns the currently selected oversampling. Range: 0..3 |
| 331 | */ |
| 332 | static unsigned char bmp18x_get_oversampling(struct bmp18x_data *data) |
| 333 | { |
| 334 | return data->oversampling_setting; |
| 335 | } |
| 336 | |
| 337 | /* sysfs callbacks */ |
| 338 | static ssize_t set_oversampling(struct device *dev, |
| 339 | struct device_attribute *attr, |
| 340 | const char *buf, size_t count) |
| 341 | { |
| 342 | struct bmp18x_data *data = dev_get_drvdata(dev); |
| 343 | unsigned long oversampling; |
| 344 | int success = kstrtoul(buf, 10, &oversampling); |
| 345 | if (success == 0) { |
| 346 | mutex_lock(&data->lock); |
| 347 | bmp18x_set_oversampling(data, oversampling); |
| 348 | if (oversampling != 3) |
| 349 | data->sw_oversampling_setting = 0; |
| 350 | mutex_unlock(&data->lock); |
| 351 | return count; |
| 352 | } |
| 353 | return success; |
| 354 | } |
| 355 | |
| 356 | static ssize_t show_oversampling(struct device *dev, |
| 357 | struct device_attribute *attr, char *buf) |
| 358 | { |
| 359 | struct bmp18x_data *data = dev_get_drvdata(dev); |
| 360 | return snprintf(buf, PAGE_SIZE, |
| 361 | "%u\n", bmp18x_get_oversampling(data)); |
| 362 | } |
| 363 | static DEVICE_ATTR(oversampling, S_IWUSR | S_IRUGO, |
| 364 | show_oversampling, set_oversampling); |
| 365 | |
| 366 | static ssize_t set_sw_oversampling(struct device *dev, |
| 367 | struct device_attribute *attr, |
| 368 | const char *buf, size_t count) |
| 369 | { |
| 370 | struct bmp18x_data *data = dev_get_drvdata(dev); |
| 371 | unsigned long sw_oversampling; |
| 372 | int success = kstrtoul(buf, 10, &sw_oversampling); |
| 373 | if (success == 0) { |
| 374 | mutex_lock(&data->lock); |
| 375 | data->sw_oversampling_setting = sw_oversampling ? 1 : 0; |
| 376 | mutex_unlock(&data->lock); |
| 377 | } |
| 378 | return success; |
| 379 | } |
| 380 | |
| 381 | static ssize_t show_sw_oversampling(struct device *dev, |
| 382 | struct device_attribute *attr, char *buf) |
| 383 | { |
| 384 | struct bmp18x_data *data = dev_get_drvdata(dev); |
| 385 | return snprintf(buf, PAGE_SIZE, |
| 386 | "%u\n", data->sw_oversampling_setting); |
| 387 | } |
| 388 | static DEVICE_ATTR(sw_oversampling, S_IWUSR | S_IRUGO, |
| 389 | show_sw_oversampling, set_sw_oversampling); |
| 390 | |
| 391 | static ssize_t show_delay(struct device *dev, |
| 392 | struct device_attribute *attr, char *buf) |
| 393 | { |
| 394 | struct bmp18x_data *data = dev_get_drvdata(dev); |
| 395 | return snprintf(buf, PAGE_SIZE, "%u\n", data->delay); |
| 396 | } |
| 397 | |
| 398 | static ssize_t set_delay(struct device *dev, |
| 399 | struct device_attribute *attr, |
| 400 | const char *buf, size_t count) |
| 401 | { |
| 402 | struct bmp18x_data *data = dev_get_drvdata(dev); |
| 403 | unsigned long delay; |
| 404 | int success = kstrtoul(buf, 10, &delay); |
| 405 | if (success == 0) { |
| 406 | mutex_lock(&data->lock); |
| 407 | data->delay = delay; |
| 408 | mutex_unlock(&data->lock); |
| 409 | } |
| 410 | return success; |
| 411 | } |
| 412 | static DEVICE_ATTR(delay, S_IWUSR | S_IRUGO, |
| 413 | show_delay, set_delay); |
| 414 | |
| 415 | static ssize_t show_enable(struct device *dev, |
| 416 | struct device_attribute *attr, char *buf) |
| 417 | { |
| 418 | struct bmp18x_data *data = dev_get_drvdata(dev); |
| 419 | return snprintf(buf, PAGE_SIZE, "%u\n", data->enable); |
| 420 | } |
| 421 | |
| 422 | static ssize_t set_enable(struct device *dev, |
| 423 | struct device_attribute *attr, |
| 424 | const char *buf, size_t count) |
| 425 | { |
| 426 | struct bmp18x_data *data = dev_get_drvdata(dev); |
| 427 | unsigned long enable; |
| 428 | int success = kstrtoul(buf, 10, &enable); |
| 429 | if (success == 0) { |
| 430 | mutex_lock(&data->lock); |
| 431 | data->enable = enable ? 1 : 0; |
| 432 | |
| 433 | if (data->enable) { |
| 434 | bmp18x_enable(dev); |
| 435 | schedule_delayed_work(&data->work, |
| 436 | msecs_to_jiffies(data->delay)); |
| 437 | } else { |
| 438 | cancel_delayed_work_sync(&data->work); |
| 439 | bmp18x_disable(dev); |
| 440 | } |
| 441 | mutex_unlock(&data->lock); |
| 442 | |
| 443 | } |
Richard Liu | 47d071d | 2013-04-14 14:08:16 -0700 | [diff] [blame] | 444 | return count; |
hongji.zhou@cn.bosch.com | f1204ad | 2013-02-05 14:45:04 +0800 | [diff] [blame] | 445 | } |
| 446 | static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, |
| 447 | show_enable, set_enable); |
| 448 | |
| 449 | static ssize_t show_temperature(struct device *dev, |
| 450 | struct device_attribute *attr, char *buf) |
| 451 | { |
| 452 | int temperature; |
| 453 | int status; |
| 454 | struct bmp18x_data *data = dev_get_drvdata(dev); |
| 455 | |
| 456 | status = bmp18x_get_temperature(data, &temperature); |
| 457 | if (status != 0) |
| 458 | return status; |
| 459 | else |
| 460 | return snprintf(buf, PAGE_SIZE, |
| 461 | "%d\n", temperature); |
| 462 | } |
| 463 | static DEVICE_ATTR(temp0_input, S_IRUGO, show_temperature, NULL); |
| 464 | |
| 465 | |
| 466 | static ssize_t show_pressure(struct device *dev, |
| 467 | struct device_attribute *attr, char *buf) |
| 468 | { |
| 469 | int pressure; |
| 470 | int status; |
| 471 | struct bmp18x_data *data = dev_get_drvdata(dev); |
| 472 | |
| 473 | status = bmp18x_get_pressure(data, &pressure); |
| 474 | if (status != 0) |
| 475 | return status; |
| 476 | else |
| 477 | return snprintf(buf, PAGE_SIZE, "%d\n", pressure); |
| 478 | } |
| 479 | static DEVICE_ATTR(pressure0_input, S_IRUGO, show_pressure, NULL); |
| 480 | |
| 481 | |
| 482 | static struct attribute *bmp18x_attributes[] = { |
| 483 | &dev_attr_temp0_input.attr, |
| 484 | &dev_attr_pressure0_input.attr, |
| 485 | &dev_attr_oversampling.attr, |
| 486 | &dev_attr_sw_oversampling.attr, |
| 487 | &dev_attr_delay.attr, |
| 488 | &dev_attr_enable.attr, |
| 489 | NULL |
| 490 | }; |
| 491 | |
| 492 | static const struct attribute_group bmp18x_attr_group = { |
| 493 | .attrs = bmp18x_attributes, |
| 494 | }; |
| 495 | |
| 496 | static void bmp18x_work_func(struct work_struct *work) |
| 497 | { |
| 498 | struct bmp18x_data *client_data = |
| 499 | container_of((struct delayed_work *)work, |
| 500 | struct bmp18x_data, work); |
| 501 | unsigned long delay = msecs_to_jiffies(client_data->delay); |
| 502 | unsigned long j1 = jiffies; |
| 503 | int pressure; |
| 504 | int status; |
| 505 | |
| 506 | status = bmp18x_get_pressure(client_data, &pressure); |
| 507 | |
| 508 | if (status == 0) { |
| 509 | input_report_abs(client_data->input, ABS_PRESSURE, pressure); |
| 510 | input_sync(client_data->input); |
| 511 | } |
| 512 | |
| 513 | schedule_delayed_work(&client_data->work, delay-(jiffies-j1)); |
| 514 | } |
| 515 | |
| 516 | static int bmp18x_input_init(struct bmp18x_data *data) |
| 517 | { |
| 518 | struct input_dev *dev; |
| 519 | int err; |
| 520 | |
| 521 | dev = input_allocate_device(); |
| 522 | if (!dev) |
| 523 | return -ENOMEM; |
| 524 | dev->name = BMP18X_NAME; |
| 525 | dev->id.bustype = BUS_I2C; |
| 526 | |
| 527 | input_set_capability(dev, EV_ABS, ABS_MISC); |
| 528 | input_set_abs_params(dev, ABS_PRESSURE, |
| 529 | ABS_MIN_PRESSURE, ABS_MAX_PRESSURE, 0, 0); |
| 530 | input_set_drvdata(dev, data); |
| 531 | |
| 532 | err = input_register_device(dev); |
| 533 | if (err < 0) { |
| 534 | input_free_device(dev); |
| 535 | return err; |
| 536 | } |
| 537 | data->input = dev; |
| 538 | |
| 539 | return 0; |
| 540 | } |
| 541 | |
| 542 | static void bmp18x_input_delete(struct bmp18x_data *data) |
| 543 | { |
| 544 | struct input_dev *dev = data->input; |
| 545 | |
| 546 | input_unregister_device(dev); |
| 547 | input_free_device(dev); |
| 548 | } |
| 549 | |
| 550 | static int bmp18x_init_client(struct bmp18x_data *data, |
| 551 | struct bmp18x_platform_data *pdata) |
| 552 | { |
| 553 | int status = bmp18x_read_calibration_data(data); |
| 554 | if (status != 0) |
| 555 | goto exit; |
| 556 | data->last_temp_measurement = 0; |
| 557 | data->temp_measurement_period = |
| 558 | pdata ? (pdata->temp_measurement_period/1000)*HZ : 1*HZ; |
| 559 | data->oversampling_setting = pdata ? pdata->default_oversampling : 3; |
| 560 | if (data->oversampling_setting == 3) |
| 561 | data->sw_oversampling_setting |
| 562 | = pdata ? pdata->default_sw_oversampling : 0; |
| 563 | mutex_init(&data->lock); |
| 564 | exit: |
| 565 | return status; |
| 566 | } |
| 567 | |
| 568 | __devinit int bmp18x_probe(struct device *dev, struct bmp18x_data_bus *data_bus) |
| 569 | { |
| 570 | struct bmp18x_data *data; |
| 571 | struct bmp18x_platform_data *pdata = dev->platform_data; |
| 572 | u8 chip_id = pdata && pdata->chip_id ? pdata->chip_id : BMP18X_CHIP_ID; |
| 573 | int err = 0; |
| 574 | |
| 575 | if (pdata && pdata->init_hw) { |
Richard Liu | 47d071d | 2013-04-14 14:08:16 -0700 | [diff] [blame] | 576 | err = pdata->init_hw(data_bus); |
hongji.zhou@cn.bosch.com | f1204ad | 2013-02-05 14:45:04 +0800 | [diff] [blame] | 577 | if (err) { |
| 578 | printk(KERN_ERR "%s: init_hw failed!\n", |
| 579 | BMP18X_NAME); |
| 580 | goto exit; |
| 581 | } |
| 582 | } |
| 583 | |
| 584 | if (data_bus->bops->read_byte(data_bus->client, |
| 585 | BMP18X_CHIP_ID_REG) != chip_id) { |
| 586 | printk(KERN_ERR "%s: chip_id failed!\n", BMP18X_NAME); |
| 587 | err = -ENODEV; |
| 588 | goto exit; |
| 589 | } |
| 590 | |
| 591 | data = kzalloc(sizeof(struct bmp18x_data), GFP_KERNEL); |
| 592 | if (!data) { |
| 593 | err = -ENOMEM; |
| 594 | goto exit; |
| 595 | } |
| 596 | |
| 597 | dev_set_drvdata(dev, data); |
| 598 | data->data_bus = *data_bus; |
| 599 | data->dev = dev; |
| 600 | |
| 601 | /* Initialize the BMP18X chip */ |
| 602 | err = bmp18x_init_client(data, pdata); |
| 603 | if (err != 0) |
| 604 | goto exit_free; |
| 605 | |
| 606 | /* Initialize the BMP18X input device */ |
| 607 | err = bmp18x_input_init(data); |
| 608 | if (err != 0) |
| 609 | goto exit_free; |
| 610 | |
| 611 | /* Register sysfs hooks */ |
| 612 | err = sysfs_create_group(&data->input->dev.kobj, &bmp18x_attr_group); |
| 613 | if (err) |
| 614 | goto error_sysfs; |
| 615 | /* workqueue init */ |
| 616 | INIT_DELAYED_WORK(&data->work, bmp18x_work_func); |
| 617 | data->delay = BMP_DELAY_DEFAULT; |
| 618 | data->enable = 0; |
| 619 | |
| 620 | #ifdef CONFIG_HAS_EARLYSUSPEND |
| 621 | data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; |
| 622 | data->early_suspend.suspend = bmp18x_early_suspend; |
| 623 | data->early_suspend.resume = bmp18x_late_resume; |
| 624 | register_early_suspend(&data->early_suspend); |
| 625 | #endif |
| 626 | |
| 627 | dev_info(dev, "Succesfully initialized bmp18x!\n"); |
| 628 | return 0; |
| 629 | |
| 630 | error_sysfs: |
| 631 | bmp18x_input_delete(data); |
| 632 | exit_free: |
| 633 | kfree(data); |
| 634 | exit: |
| 635 | if (pdata && pdata->deinit_hw) |
Richard Liu | 47d071d | 2013-04-14 14:08:16 -0700 | [diff] [blame] | 636 | pdata->deinit_hw(data_bus); |
hongji.zhou@cn.bosch.com | f1204ad | 2013-02-05 14:45:04 +0800 | [diff] [blame] | 637 | return err; |
| 638 | } |
| 639 | EXPORT_SYMBOL(bmp18x_probe); |
| 640 | |
| 641 | int bmp18x_remove(struct device *dev) |
| 642 | { |
| 643 | struct bmp18x_data *data = dev_get_drvdata(dev); |
| 644 | #ifdef CONFIG_HAS_EARLYSUSPEND |
| 645 | unregister_early_suspend(&data->early_suspend); |
| 646 | #endif |
| 647 | sysfs_remove_group(&data->input->dev.kobj, &bmp18x_attr_group); |
| 648 | kfree(data); |
| 649 | |
| 650 | return 0; |
| 651 | } |
| 652 | EXPORT_SYMBOL(bmp18x_remove); |
| 653 | |
| 654 | #ifdef CONFIG_PM |
| 655 | int bmp18x_disable(struct device *dev) |
| 656 | { |
| 657 | struct bmp18x_platform_data *pdata = dev->platform_data; |
Richard Liu | 47d071d | 2013-04-14 14:08:16 -0700 | [diff] [blame] | 658 | struct bmp18x_data *data = dev_get_drvdata(dev); |
hongji.zhou@cn.bosch.com | f1204ad | 2013-02-05 14:45:04 +0800 | [diff] [blame] | 659 | if (pdata && pdata->deinit_hw) |
Richard Liu | 47d071d | 2013-04-14 14:08:16 -0700 | [diff] [blame] | 660 | pdata->deinit_hw(&data->data_bus); |
hongji.zhou@cn.bosch.com | f1204ad | 2013-02-05 14:45:04 +0800 | [diff] [blame] | 661 | |
| 662 | return 0; |
| 663 | } |
| 664 | EXPORT_SYMBOL(bmp18x_disable); |
| 665 | |
| 666 | int bmp18x_enable(struct device *dev) |
| 667 | { |
| 668 | struct bmp18x_platform_data *pdata = dev->platform_data; |
Richard Liu | 47d071d | 2013-04-14 14:08:16 -0700 | [diff] [blame] | 669 | struct bmp18x_data *data = dev_get_drvdata(dev); |
hongji.zhou@cn.bosch.com | f1204ad | 2013-02-05 14:45:04 +0800 | [diff] [blame] | 670 | if (pdata && pdata->init_hw) |
Richard Liu | 47d071d | 2013-04-14 14:08:16 -0700 | [diff] [blame] | 671 | return pdata->init_hw(&data->data_bus); |
hongji.zhou@cn.bosch.com | f1204ad | 2013-02-05 14:45:04 +0800 | [diff] [blame] | 672 | |
| 673 | return 0; |
| 674 | } |
| 675 | EXPORT_SYMBOL(bmp18x_enable); |
| 676 | #endif |
| 677 | |
| 678 | #ifdef CONFIG_HAS_EARLYSUSPEND |
| 679 | static void bmp18x_early_suspend(struct early_suspend *h) |
| 680 | { |
| 681 | struct bmp18x_data *data = |
| 682 | container_of(h, struct bmp18x_data, early_suspend); |
| 683 | if (data->enable) { |
| 684 | cancel_delayed_work_sync(&data->work); |
| 685 | (void) bmp18x_disable(data->dev); |
| 686 | } |
| 687 | } |
| 688 | |
| 689 | static void bmp18x_late_resume(struct early_suspend *h) |
| 690 | { |
| 691 | struct bmp18x_data *data = |
| 692 | container_of(h, struct bmp18x_data, early_suspend); |
| 693 | |
| 694 | if (data->enable) { |
| 695 | (void) bmp18x_enable(data->dev); |
| 696 | schedule_delayed_work(&data->work, |
| 697 | msecs_to_jiffies(data->delay)); |
| 698 | } |
| 699 | |
| 700 | } |
| 701 | #endif |
| 702 | |
| 703 | MODULE_AUTHOR("Eric Andersson <eric.andersson@unixphere.com>"); |
| 704 | MODULE_DESCRIPTION("BMP18X driver"); |
| 705 | MODULE_LICENSE("GPL"); |