1 /* Driver for the BMP085 Preassure and Temperature Sensor */ 2 3 #include <minix/ds.h> 4 #include <minix/drivers.h> 5 #include <minix/i2c.h> 6 #include <minix/i2cdriver.h> 7 #include <minix/chardriver.h> 8 #include <minix/log.h> 9 10 /* Control Register for triggering a measurement */ 11 #define CTRL_REG 0xf4 12 13 /* temperature sensor - it only has one 'mode' - conversion time 4.5 ms */ 14 #define CMD_TRIG_T 0x2e 15 #define UDELAY_T (4500) 16 17 /* pressure sensor - ultra low power mode - conversion time 4.5 ms */ 18 #define CMD_TRIG_P_ULP 0x34 19 #define MODE_ULP 0x00 20 #define UDELAY_ULP (4500) 21 22 /* pressure sensor - standard mode - conversion time 7.5 ms */ 23 #define CMD_TRIG_P_STD 0x74 24 #define MODE_STD 0x01 25 #define UDELAY_STD (7500) 26 27 /* pressure sensor - high resolution mode - conversion time 13.5 ms */ 28 #define CMD_TRIG_P_HR 0xb4 29 #define MODE_HR 0x02 30 #define UDELAY_HR (13500) 31 32 /* pressure sensor - ultra high resolution mode - conversion time 25.5 ms */ 33 #define CMD_TRIG_P_UHR 0xf4 34 #define MODE_UHR 0x03 35 #define UDELAY_UHR (25500) 36 37 /* Values for the different modes of operation */ 38 struct pressure_cmd 39 { 40 uint8_t cmd; 41 uint8_t mode; 42 uint16_t udelay; 43 }; 44 45 /* Table of available modes and their parameters. */ 46 static struct pressure_cmd pressure_cmds[4] = { 47 {CMD_TRIG_P_ULP, MODE_ULP, UDELAY_ULP}, 48 {CMD_TRIG_P_STD, MODE_STD, UDELAY_STD}, 49 {CMD_TRIG_P_HR, MODE_HR, UDELAY_HR}, 50 {CMD_TRIG_P_UHR, MODE_UHR, UDELAY_UHR} 51 }; 52 53 /* Default to standard mode. 54 * There isn't code to configure the resolution at runtime, but it should 55 * easy to implement by setting p_cmd to the right element of pressure_cmds. 56 */ 57 static struct pressure_cmd *p_cmd = &pressure_cmds[MODE_STD]; 58 59 /* Chip Identification */ 60 #define CHIPID_REG 0xd0 61 #define BMP085_CHIPID 0x55 62 63 /* 64 * There is also a version register at 0xd1, but documentation seems to be 65 * lacking. The sample code says high 4 bytes are AL version and low 4 are ML. 66 */ 67 68 /* Calibration coefficients 69 * 70 * These are unique to each chip and must be read when starting the driver. 71 * Validate them by checking that none are 0x0000 nor 0xffff. Types and 72 * names are from the datasheet. 73 */ 74 struct calibration 75 { 76 int16_t ac1; 77 int16_t ac2; 78 int16_t ac3; 79 uint16_t ac4; 80 uint16_t ac5; 81 uint16_t ac6; 82 int16_t b1; 83 int16_t b2; 84 int16_t mb; 85 int16_t mc; 86 int16_t md; 87 } cal; 88 89 /* Register locations for calibration coefficients */ 90 #define AC1_MSB_REG 0xaa 91 #define AC1_LSB_REG 0xab 92 #define AC2_MSB_REG 0xac 93 #define AC2_LSB_REG 0xad 94 #define AC3_MSB_REG 0xae 95 #define AC3_LSB_REG 0xaf 96 #define AC4_MSB_REG 0xb0 97 #define AC4_LSB_REG 0xb1 98 #define AC5_MSB_REG 0xb2 99 #define AC5_LSB_REG 0xb3 100 #define AC6_MSB_REG 0xb4 101 #define AC6_LSB_REG 0xb5 102 #define B1_MSB_REG 0xb6 103 #define B1_LSB_REG 0xb7 104 #define B2_MSB_REG 0xb8 105 #define B2_LSB_REG 0xb9 106 #define MB_MSB_REG 0xba 107 #define MB_LSB_REG 0xbb 108 #define MC_MSB_REG 0xbc 109 #define MC_LSB_REG 0xbd 110 #define MD_MSB_REG 0xbe 111 #define MD_LSB_REG 0xbf 112 113 #define CAL_COEF_FIRST AC1_MSB_REG 114 #define CAL_COEF_LAST MD_LSB_REG 115 116 #define CAL_COEF_IS_VALID(x) (x != 0x0000 && x != 0xffff) 117 118 #define SENSOR_VAL_MSB_REG 0xf6 119 #define SENSOR_VAL_LSB_REG 0xf7 120 #define SENSOR_VAL_XLSB_REG 0xf8 121 122 /* logging - use with log_warn(), log_info(), log_debug(), log_trace(), etc */ 123 static struct log log = { 124 .name = "bmp085", 125 .log_level = LEVEL_INFO, 126 .log_func = default_log 127 }; 128 129 /* Only one valid slave address. It isn't configurable. */ 130 static i2c_addr_t valid_addrs[5] = { 131 0x77, 0x00 132 }; 133 134 /* Buffer to store output string returned when reading from device file. */ 135 #define BUFFER_LEN 64 136 char buffer[BUFFER_LEN + 1]; 137 138 /* the bus that this device is on (counting starting at 1) */ 139 static uint32_t bus; 140 141 /* slave address of the device */ 142 static i2c_addr_t address; 143 144 /* endpoint for the driver for the bus itself. */ 145 static endpoint_t bus_endpoint; 146 147 /* main device functions */ 148 static int bmp085_init(void); 149 static int version_check(void); 150 static int read_cal_coef(void); 151 static int measure(int32_t * temperature, int32_t * pressure); 152 153 /* libchardriver callbacks */ 154 static ssize_t bmp085_read(devminor_t minor, u64_t position, endpoint_t endpt, 155 cp_grant_id_t grant, size_t size, int flags, cdev_id_t id); 156 static void bmp085_other(message * m, int ipc_status); 157 158 /* SEF Function */ 159 static int sef_cb_lu_state_save(int); 160 static int lu_state_restore(void); 161 static int sef_cb_init(int type, sef_init_info_t * info); 162 static void sef_local_startup(void); 163 164 /* Entry points to this driver from libchardriver. */ 165 static struct chardriver bmp085_tab = { 166 .cdr_read = bmp085_read, 167 .cdr_other = bmp085_other 168 }; 169 170 /* 171 * Initialize the driver. Checks the CHIPID against a known value and 172 * reads the calibration coefficients. 173 * 174 * The chip does have a soft reset register (0xe0), but there 175 * doesn't appear to be any documentation or example usage for it. 176 */ 177 static int 178 bmp085_init(void) 179 { 180 int r; 181 int32_t t, p; 182 183 r = version_check(); 184 if (r != OK) { 185 return EXIT_FAILURE; 186 } 187 188 r = read_cal_coef(); 189 if (r != OK) { 190 return EXIT_FAILURE; 191 } 192 193 return OK; 194 } 195 196 static int 197 version_check(void) 198 { 199 int r; 200 uint8_t chipid; 201 202 r = i2creg_read8(bus_endpoint, address, CHIPID_REG, &chipid); 203 if (r != OK) { 204 log_warn(&log, "Couldn't read CHIPID\n"); 205 return -1; 206 } 207 208 if (chipid != BMP085_CHIPID) { 209 log_warn(&log, "Bad CHIPID\n"); 210 return -1; 211 } 212 213 log_debug(&log, "CHIPID OK\n"); 214 215 return OK; 216 } 217 218 /* 219 * Read the calibration data from the chip. Each individual chip has a unique 220 * set of calibration parameters that get used to compute the true temperature 221 * and pressure. 222 */ 223 static int 224 read_cal_coef(void) 225 { 226 int r; 227 228 /* Populate the calibration struct with values */ 229 r = i2creg_read16(bus_endpoint, address, AC1_MSB_REG, &cal.ac1); 230 if (r != OK) { 231 return -1; 232 } 233 log_debug(&log, "cal.ac1 = %d\n", cal.ac1); 234 235 r = i2creg_read16(bus_endpoint, address, AC2_MSB_REG, &cal.ac2); 236 if (r != OK) { 237 return -1; 238 } 239 log_debug(&log, "cal.ac2 = %d\n", cal.ac2); 240 241 r = i2creg_read16(bus_endpoint, address, AC3_MSB_REG, &cal.ac3); 242 if (r != OK) { 243 return -1; 244 } 245 log_debug(&log, "cal.ac3 = %d\n", cal.ac3); 246 247 r = i2creg_read16(bus_endpoint, address, AC4_MSB_REG, &cal.ac4); 248 if (r != OK) { 249 return -1; 250 } 251 log_debug(&log, "cal.ac4 = %u\n", cal.ac4); 252 253 r = i2creg_read16(bus_endpoint, address, AC5_MSB_REG, &cal.ac5); 254 if (r != OK) { 255 return -1; 256 } 257 log_debug(&log, "cal.ac5 = %u\n", cal.ac5); 258 259 r = i2creg_read16(bus_endpoint, address, AC6_MSB_REG, &cal.ac6); 260 if (r != OK) { 261 return -1; 262 } 263 log_debug(&log, "cal.ac6 = %u\n", cal.ac6); 264 265 r = i2creg_read16(bus_endpoint, address, B1_MSB_REG, &cal.b1); 266 if (r != OK) { 267 return -1; 268 } 269 log_debug(&log, "cal.b1 = %d\n", cal.b1); 270 271 r = i2creg_read16(bus_endpoint, address, B2_MSB_REG, &cal.b2); 272 if (r != OK) { 273 return -1; 274 } 275 log_debug(&log, "cal.b2 = %d\n", cal.b2); 276 277 r = i2creg_read16(bus_endpoint, address, MB_MSB_REG, &cal.mb); 278 if (r != OK) { 279 return -1; 280 } 281 log_debug(&log, "cal.mb = %d\n", cal.mb); 282 283 r = i2creg_read16(bus_endpoint, address, MC_MSB_REG, &cal.mc); 284 if (r != OK) { 285 return -1; 286 } 287 log_debug(&log, "cal.mc = %d\n", cal.mc); 288 289 r = i2creg_read16(bus_endpoint, address, MD_MSB_REG, &cal.md); 290 if (r != OK) { 291 return -1; 292 } 293 log_debug(&log, "cal.md = %d\n", cal.md); 294 295 /* Validate. Data sheet says values should not be 0x0000 nor 0xffff */ 296 if (!CAL_COEF_IS_VALID(cal.ac1) || 297 !CAL_COEF_IS_VALID(cal.ac2) || 298 !CAL_COEF_IS_VALID(cal.ac3) || 299 !CAL_COEF_IS_VALID(cal.ac4) || 300 !CAL_COEF_IS_VALID(cal.ac5) || 301 !CAL_COEF_IS_VALID(cal.ac6) || 302 !CAL_COEF_IS_VALID(cal.b1) || 303 !CAL_COEF_IS_VALID(cal.b2) || 304 !CAL_COEF_IS_VALID(cal.mb) || 305 !CAL_COEF_IS_VALID(cal.mc) || !CAL_COEF_IS_VALID(cal.md)) { 306 307 log_warn(&log, "Invalid calibration data found on chip.\n"); 308 return -1; 309 } 310 311 log_debug(&log, "Read Cal Data OK\n"); 312 313 return OK; 314 } 315 316 /* 317 * Measure the uncompensated temperature and uncompensated pressure from the 318 * chip and apply the formulas to determine the true temperature and pressure. 319 * Note, the data sheet is light on the details when it comes to defining the 320 * meaning of each variable, so this function has a lot of cryptic names in it. 321 */ 322 static int 323 measure(int32_t * temperature, int32_t * pressure) 324 { 325 int r; 326 327 /* Types are given in the datasheet. Their long translates to 32-bits */ 328 329 int16_t ut; /* uncompensated temperature */ 330 int32_t up; /* uncompensated pressure */ 331 int32_t x1; 332 int32_t x2; 333 int32_t x3; 334 int32_t b3; 335 uint32_t b4; 336 int32_t b5; 337 int32_t b6; 338 uint32_t b7; 339 int32_t t; /* true temperature (in 0.1C) */ 340 int32_t p; /* true pressure (in Pa) */ 341 342 log_debug(&log, "Triggering Temp Reading...\n"); 343 344 /* trigger temperature reading */ 345 r = i2creg_write8(bus_endpoint, address, CTRL_REG, CMD_TRIG_T); 346 if (r != OK) { 347 log_warn(&log, "Failed to trigger temperature reading.\n"); 348 return -1; 349 } 350 351 /* wait for sampling to be completed. */ 352 micro_delay(UDELAY_T); 353 354 /* read the uncompensated temperature */ 355 r = i2creg_read16(bus_endpoint, address, SENSOR_VAL_MSB_REG, &ut); 356 if (r != OK) { 357 log_warn(&log, "Failed to read temperature.\n"); 358 return -1; 359 } 360 361 log_debug(&log, "ut = %d\n", ut); 362 363 log_debug(&log, "Triggering Pressure Reading...\n"); 364 365 /* trigger pressure reading */ 366 r = i2creg_write8(bus_endpoint, address, CTRL_REG, p_cmd->cmd); 367 if (r != OK) { 368 log_warn(&log, "Failed to trigger pressure reading.\n"); 369 return -1; 370 } 371 372 /* wait for sampling to be completed. */ 373 micro_delay(p_cmd->udelay); 374 375 /* read the uncompensated pressure */ 376 r = i2creg_read24(bus_endpoint, address, SENSOR_VAL_MSB_REG, &up); 377 if (r != OK) { 378 log_warn(&log, "Failed to read pressure.\n"); 379 return -1; 380 } 381 382 /* shift by 8 - oversampling setting */ 383 up = (up >> (8 - p_cmd->mode)); 384 385 log_debug(&log, "up = %d\n", up); 386 387 /* convert uncompensated temperature to true temperature */ 388 x1 = ((ut - cal.ac6) * cal.ac5) / (1 << 15); 389 x2 = (cal.mc * (1 << 11)) / (x1 + cal.md); 390 b5 = x1 + x2; 391 t = (b5 + 8) / (1 << 4); 392 393 /* save the result */ 394 *temperature = t; 395 396 log_debug(&log, "t = %d\n", t); 397 398 /* Convert uncompensated pressure to true pressure. 399 * This is really how the data sheet suggests doing it. 400 * There is no alternative approach suggested. Other open 401 * source drivers I've found use this method. 402 */ 403 b6 = b5 - 4000; 404 x1 = ((cal.b2 * ((b6 * b6) >> 12)) >> 11); 405 x2 = ((cal.ac2 * b6) >> 11); 406 x3 = x1 + x2; 407 b3 = (((((cal.ac1 * 4) + x3) << p_cmd->mode) + 2) >> 2); 408 x1 = ((cal.ac3 * b6) >> 13); 409 x2 = ((cal.b1 * ((b6 * b6) >> 12)) >> 16); 410 x3 = (((x1 + x2) + 2) >> 2); 411 b4 = ((cal.ac4 * ((uint32_t) (x3 + 32768))) >> 15); 412 b7 = ((uint32_t) up - b3) * (50000 >> p_cmd->mode); 413 p = (b7 < 0x80000000) ? (b7 * 2) / b4 : (b7 / b4) * 2; 414 x1 = (p >> 8) * (p >> 8); 415 x1 = ((x1 * 3038) >> 16); 416 x2 = ((-7357 * p) >> 16); 417 p = p + ((x1 + x2 + 3791) >> 4); 418 419 *pressure = p; 420 421 log_debug(&log, "p = %d\n", p); 422 423 return OK; 424 } 425 426 static ssize_t 427 bmp085_read(devminor_t UNUSED(minor), u64_t position, endpoint_t endpt, 428 cp_grant_id_t grant, size_t size, int UNUSED(flags), cdev_id_t UNUSED(id)) 429 { 430 u64_t dev_size; 431 int r; 432 uint32_t temperature, pressure; 433 434 r = measure(&temperature, &pressure); 435 if (r != OK) { 436 return EIO; 437 } 438 439 memset(buffer, '\0', BUFFER_LEN + 1); 440 snprintf(buffer, BUFFER_LEN, "%-16s: %d.%01d\n%-16s: %d\n", 441 "TEMPERATURE", temperature / 10, temperature % 10, "PRESSURE", 442 pressure); 443 444 log_trace(&log, "%s", buffer); 445 446 dev_size = (u64_t)strlen(buffer); 447 if (position >= dev_size) return 0; 448 if (position + size > dev_size) 449 size = (size_t)(dev_size - position); 450 451 r = sys_safecopyto(endpt, grant, 0, 452 (vir_bytes)(buffer + (size_t)position), size); 453 454 return (r != OK) ? r : size; 455 } 456 457 static void 458 bmp085_other(message * m, int ipc_status) 459 { 460 int r; 461 462 if (is_ipc_notify(ipc_status)) { 463 if (m->m_source == DS_PROC_NR) { 464 log_debug(&log, 465 "bus driver changed state, update endpoint\n"); 466 i2cdriver_handle_bus_update(&bus_endpoint, bus, 467 address); 468 } 469 return; 470 } 471 472 log_warn(&log, "Invalid message type (0x%x)\n", m->m_type); 473 } 474 475 static int 476 sef_cb_lu_state_save(int UNUSED(state)) 477 { 478 ds_publish_u32("bus", bus, DSF_OVERWRITE); 479 ds_publish_u32("address", address, DSF_OVERWRITE); 480 return OK; 481 } 482 483 static int 484 lu_state_restore(void) 485 { 486 /* Restore the state. */ 487 u32_t value; 488 489 ds_retrieve_u32("bus", &value); 490 ds_delete_u32("bus"); 491 bus = (int) value; 492 493 ds_retrieve_u32("address", &value); 494 ds_delete_u32("address"); 495 address = (int) value; 496 497 return OK; 498 } 499 500 static int 501 sef_cb_init(int type, sef_init_info_t * UNUSED(info)) 502 { 503 int r; 504 505 if (type == SEF_INIT_LU) { 506 /* Restore the state. */ 507 lu_state_restore(); 508 } 509 510 /* look-up the endpoint for the bus driver */ 511 bus_endpoint = i2cdriver_bus_endpoint(bus); 512 if (bus_endpoint == 0) { 513 log_warn(&log, "Couldn't find bus driver.\n"); 514 return EXIT_FAILURE; 515 } 516 517 /* claim the device */ 518 r = i2cdriver_reserve_device(bus_endpoint, address); 519 if (r != OK) { 520 log_warn(&log, "Couldn't reserve device 0x%x (r=%d)\n", 521 address, r); 522 return EXIT_FAILURE; 523 } 524 525 r = bmp085_init(); 526 if (r != OK) { 527 log_warn(&log, "Couldn't initialize device\n"); 528 return EXIT_FAILURE; 529 } 530 531 if (type != SEF_INIT_LU) { 532 533 /* sign up for updates about the i2c bus going down/up */ 534 r = i2cdriver_subscribe_bus_updates(bus); 535 if (r != OK) { 536 log_warn(&log, "Couldn't subscribe to bus updates\n"); 537 return EXIT_FAILURE; 538 } 539 540 i2cdriver_announce(bus); 541 log_debug(&log, "announced\n"); 542 } 543 544 return OK; 545 } 546 547 static void 548 sef_local_startup(void) 549 { 550 /* 551 * Register init callbacks. Use the same function for all event types 552 */ 553 sef_setcb_init_fresh(sef_cb_init); 554 sef_setcb_init_lu(sef_cb_init); 555 sef_setcb_init_restart(sef_cb_init); 556 557 /* 558 * Register live update callbacks. 559 */ 560 /* Agree to update immediately when LU is requested in a valid state. */ 561 sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready); 562 /* Support live update starting from any standard state. */ 563 sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard); 564 /* Register a custom routine to save the state. */ 565 sef_setcb_lu_state_save(sef_cb_lu_state_save); 566 567 /* Let SEF perform startup. */ 568 sef_startup(); 569 } 570 571 int 572 main(int argc, char *argv[]) 573 { 574 int r; 575 576 env_setargs(argc, argv); 577 578 r = i2cdriver_env_parse(&bus, &address, valid_addrs); 579 if (r < 0) { 580 log_warn(&log, "Expecting -args 'bus=X address=0x77'\n"); 581 log_warn(&log, "Example -args 'bus=1 address=0x77'\n"); 582 return EXIT_FAILURE; 583 } else if (r > 0) { 584 log_warn(&log, 585 "Invalid slave address for device, expecting 0x77\n"); 586 return EXIT_FAILURE; 587 } 588 589 sef_local_startup(); 590 591 chardriver_task(&bmp085_tab); 592 593 return 0; 594 } 595