/* $License: Copyright (C) 2010 InvenSense Corporation, All Rights Reserved. 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, see . $ */ /** * @addtogroup MLDL * * @{ * @file mldl_cfg.c * @brief The Motion Library Driver Layer. */ /* ------------------ */ /* - Include Files. - */ /* ------------------ */ #include #include "mldl_cfg.h" #include "mpu.h" #include "mlsl.h" #include "mlos.h" #include "log.h" #include "mpu-accel.h" #undef MPL_LOG_TAG #define MPL_LOG_TAG "mldl_cfg:" /* --------------------- */ /* - Variables. - */ /* --------------------- */ #ifdef M_HW #define SLEEP 0 #define WAKE_UP 7 #define RESET 1 #define STANDBY 1 #else /* licteral significance of all parameters used in MLDLPowerMgmtMPU */ #define SLEEP 1 #define WAKE_UP 0 #define RESET 1 #define STANDBY 1 #endif /*---------------------*/ /*- Prototypes. -*/ /*---------------------*/ /*----------------------*/ /*- Static Functions. -*/ /*----------------------*/ static int dmp_stop(struct mldl_cfg *mldl_cfg, void *gyro_handle) { unsigned char userCtrlReg; int result; if (!mldl_cfg->dmp_is_running) return ML_SUCCESS; result = MLSLSerialRead(gyro_handle, mldl_cfg->addr, MPUREG_USER_CTRL, 1, &userCtrlReg); ERROR_CHECK(result); userCtrlReg = (userCtrlReg & (~BIT_FIFO_EN)) | BIT_FIFO_RST; userCtrlReg = (userCtrlReg & (~BIT_DMP_EN)) | BIT_DMP_RST; result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, MPUREG_USER_CTRL, userCtrlReg); ERROR_CHECK(result); mldl_cfg->dmp_is_running = 0; return result; } /** * @brief Starts the DMP running * * @return ML_SUCCESS or non-zero error code */ static int dmp_start(struct mldl_cfg *pdata, void *mlsl_handle) { unsigned char userCtrlReg; int result; if (pdata->dmp_is_running == pdata->dmp_enable) return ML_SUCCESS; result = MLSLSerialRead(mlsl_handle, pdata->addr, MPUREG_USER_CTRL, 1, &userCtrlReg); ERROR_CHECK(result); result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, MPUREG_USER_CTRL, ((userCtrlReg & (~BIT_FIFO_EN)) | BIT_FIFO_RST)); ERROR_CHECK(result); result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, MPUREG_USER_CTRL, userCtrlReg); ERROR_CHECK(result); result = MLSLSerialRead(mlsl_handle, pdata->addr, MPUREG_USER_CTRL, 1, &userCtrlReg); ERROR_CHECK(result); if (pdata->dmp_enable) userCtrlReg |= BIT_DMP_EN; else userCtrlReg &= ~BIT_DMP_EN; if (pdata->fifo_enable) userCtrlReg |= BIT_FIFO_EN; else userCtrlReg &= ~BIT_FIFO_EN; userCtrlReg |= BIT_DMP_RST; result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, MPUREG_USER_CTRL, userCtrlReg); ERROR_CHECK(result); pdata->dmp_is_running = pdata->dmp_enable; return result; } /** * @brief enables/disables the I2C bypass to an external device * connected to MPU's secondary I2C bus. * @param enable * Non-zero to enable pass through. * @return ML_SUCCESS if successful, a non-zero error code otherwise. */ static int MLDLSetI2CBypass(struct mldl_cfg *mldl_cfg, void *mlsl_handle, unsigned char enable) { unsigned char b; int result; if ((mldl_cfg->gyro_is_bypassed && enable) || (!mldl_cfg->gyro_is_bypassed && !enable)) return ML_SUCCESS; /*---- get current 'USER_CTRL' into b ----*/ result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr, MPUREG_USER_CTRL, 1, &b); ERROR_CHECK(result); b &= ~BIT_AUX_IF_EN; if (!enable) { result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr, MPUREG_USER_CTRL, (b | BIT_AUX_IF_EN)); ERROR_CHECK(result); } else { /* Coming out of I2C is tricky due to several erratta. Do not * modify this algorithm */ /* * 1) wait for the right time and send the command to change * the aux i2c slave address to an invalid address that will * get nack'ed * * 0x00 is broadcast. 0x7F is unlikely to be used by any aux. */ result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr, MPUREG_AUX_SLV_ADDR, 0x7F); ERROR_CHECK(result); /* * 2) wait enough time for a nack to occur, then go into * bypass mode: */ MLOSSleep(2); result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr, MPUREG_USER_CTRL, (b)); ERROR_CHECK(result); /* * 3) wait for up to one MPU cycle then restore the slave * address */ MLOSSleep(SAMPLING_PERIOD_US(mldl_cfg) / 1000); result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr, MPUREG_AUX_SLV_ADDR, mldl_cfg->pdata-> accel.address); ERROR_CHECK(result); /* * 4) reset the ime interface */ #ifdef M_HW result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr, MPUREG_USER_CTRL, (b | BIT_I2C_MST_RST)); #else result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr, MPUREG_USER_CTRL, (b | BIT_AUX_IF_RST)); #endif ERROR_CHECK(result); MLOSSleep(2); } mldl_cfg->gyro_is_bypassed = enable; return result; } struct tsProdRevMap { unsigned char siliconRev; unsigned short sensTrim; }; #define NUM_OF_PROD_REVS (DIM(prodRevsMap)) /* NOTE : 'npp' is a non production part */ #ifdef M_HW #define OLDEST_PROD_REV_SUPPORTED 1 static struct tsProdRevMap prodRevsMap[] = { {0, 0}, {MPU_SILICON_REV_A1, 131}, /* 1 A1 (npp) */ {MPU_SILICON_REV_A1, 131}, /* 2 A1 (npp) */ {MPU_SILICON_REV_A1, 131}, /* 3 A1 (npp) */ {MPU_SILICON_REV_A1, 131}, /* 4 A1 (npp) */ {MPU_SILICON_REV_A1, 131}, /* 5 A1 (npp) */ {MPU_SILICON_REV_A1, 131}, /* 6 A1 (npp) */ {MPU_SILICON_REV_A1, 131}, /* 7 A1 (npp) */ {MPU_SILICON_REV_A1, 131}, /* 8 A1 (npp) */ }; #else /* !M_HW */ #define OLDEST_PROD_REV_SUPPORTED 11 static struct tsProdRevMap prodRevsMap[] = { {0, 0}, {MPU_SILICON_REV_A4, 131}, /* 1 A? OBSOLETED */ {MPU_SILICON_REV_A4, 131}, /* 2 | */ {MPU_SILICON_REV_A4, 131}, /* 3 V */ {MPU_SILICON_REV_A4, 131}, /* 4 */ {MPU_SILICON_REV_A4, 131}, /* 5 */ {MPU_SILICON_REV_A4, 131}, /* 6 */ {MPU_SILICON_REV_A4, 131}, /* 7 */ {MPU_SILICON_REV_A4, 131}, /* 8 */ {MPU_SILICON_REV_A4, 131}, /* 9 */ {MPU_SILICON_REV_A4, 131}, /* 10 */ {MPU_SILICON_REV_B1, 131}, /* 11 B1 */ {MPU_SILICON_REV_B1, 131}, /* 12 | */ {MPU_SILICON_REV_B1, 131}, /* 13 V */ {MPU_SILICON_REV_B1, 131}, /* 14 B4 */ {MPU_SILICON_REV_B4, 131}, /* 15 | */ {MPU_SILICON_REV_B4, 131}, /* 16 V */ {MPU_SILICON_REV_B4, 131}, /* 17 */ {MPU_SILICON_REV_B4, 131}, /* 18 */ {MPU_SILICON_REV_B4, 115}, /* 19 */ {MPU_SILICON_REV_B4, 115}, /* 20 */ {MPU_SILICON_REV_B6, 131}, /* 21 B6 (B6/A9) */ {MPU_SILICON_REV_B4, 115}, /* 22 B4 (B7/A10) */ {MPU_SILICON_REV_B6, 0}, /* 23 B6 (npp) */ {MPU_SILICON_REV_B6, 0}, /* 24 | (npp) */ {MPU_SILICON_REV_B6, 0}, /* 25 V (npp) */ {MPU_SILICON_REV_B6, 131}, /* 26 (B6/A11) */ }; #endif /* !M_HW */ /** * @internal * @brief Get the silicon revision ID from OTP. * The silicon revision number is in read from OTP bank 0, * ADDR6[7:2]. The corresponding ID is retrieved by lookup * in a map. * @return The silicon revision ID (0 on error). */ static int MLDLGetSiliconRev(struct mldl_cfg *pdata, void *mlsl_handle) { int result; unsigned char index = 0x00; unsigned char bank = (BIT_PRFTCH_EN | BIT_CFG_USER_BANK | MPU_MEM_OTP_BANK_0); unsigned short memAddr = ((bank << 8) | 0x06); result = MLSLSerialReadMem(mlsl_handle, pdata->addr, memAddr, 1, &index); ERROR_CHECK(result) if (result) return result; index >>= 2; /* clean the prefetch and cfg user bank bits */ result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, MPUREG_BANK_SEL, 0); ERROR_CHECK(result) if (result) return result; if (index < OLDEST_PROD_REV_SUPPORTED || NUM_OF_PROD_REVS <= index) { pdata->silicon_revision = 0; pdata->trim = 0; MPL_LOGE("Unsupported Product Revision Detected : %d\n", index); return ML_ERROR_INVALID_MODULE; } pdata->silicon_revision = prodRevsMap[index].siliconRev; pdata->trim = prodRevsMap[index].sensTrim; if (pdata->trim == 0) { MPL_LOGE("sensitivity trim is 0" " - unsupported non production part.\n"); return ML_ERROR_INVALID_MODULE; } return result; } /** * @brief Enable/Disable the use MPU's VDDIO level shifters. * When enabled the voltage interface with AUX or other external * accelerometer is using Vlogic instead of VDD (supply). * * @note Must be called after MLSerialOpen(). * @note Typically be called before MLDmpOpen(). * If called after MLDmpOpen(), must be followed by a call to * MLDLApplyLevelShifterBit() to write the setting on the hw. * * @param[in] enable * 1 to enable, 0 to disable * * @return ML_SUCCESS if successfull, a non-zero error code otherwise. **/ static int MLDLSetLevelShifterBit(struct mldl_cfg *pdata, void *mlsl_handle, unsigned char enable) { #ifndef M_HW int result; unsigned char reg; unsigned char mask; unsigned char regval; if (0 == pdata->silicon_revision) return ML_ERROR_INVALID_PARAMETER; /*-- on parts before B6 the VDDIO bit is bit 7 of ACCEL_BURST_ADDR -- NOTE: this is incompatible with ST accelerometers where the VDDIO bit MUST be set to enable ST's internal logic to autoincrement the register address on burst reads --*/ if ((pdata->silicon_revision & 0xf) < MPU_SILICON_REV_B6) { reg = MPUREG_ACCEL_BURST_ADDR; mask = 0x80; } else { /*-- on B6 parts the VDDIO bit was moved to FIFO_EN2 => the mask is always 0x04 --*/ reg = MPUREG_FIFO_EN2; mask = 0x04; } result = MLSLSerialRead(mlsl_handle, pdata->addr, reg, 1, ®val); if (result) return result; if (enable) regval |= mask; else regval &= ~mask; result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, reg, regval); return result; #else return ML_SUCCESS; #endif } #ifdef M_HW /** * @internal * @param reset 1 to reset hardware */ static tMLError mpu60xx_pwr_mgmt(struct mldl_cfg *pdata, void *mlsl_handle, unsigned char reset, unsigned char powerselection) { unsigned char b; tMLError result; if (powerselection < 0 || powerselection > 7) return ML_ERROR_INVALID_PARAMETER; result = MLSLSerialRead(mlsl_handle, pdata->addr, MPUREG_PWR_MGMT_1, 1, &b); ERROR_CHECK(result); b &= ~(BITS_PWRSEL); if (reset) { /* Current sillicon has an errata where the reset will get * nacked. Ignore the error code for now. */ result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, MPUREG_PWR_MGM, b | BIT_H_RESET); #define M_HW_RESET_ERRATTA #ifndef M_HW_RESET_ERRATTA ERROR_CHECK(result); #else MLOSSleep(50); #endif } b |= (powerselection << 4); if (b & BITS_PWRSEL) pdata->gyro_is_suspended = FALSE; else pdata->gyro_is_suspended = TRUE; result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, MPUREG_PWR_MGM, b); ERROR_CHECK(result); return ML_SUCCESS; } /** * @internal */ static tMLError MLDLStandByGyros(struct mldl_cfg *pdata, void *mlsl_handle, unsigned char disable_gx, unsigned char disable_gy, unsigned char disable_gz) { unsigned char b; tMLError result; result = MLSLSerialRead(mlsl_handle, pdata->addr, MPUREG_PWR_MGMT_2, 1, &b); ERROR_CHECK(result); b &= ~(BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG); b |= (disable_gx << 2 | disable_gy << 1 | disable_gz); result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, MPUREG_PWR_MGMT_2, b); ERROR_CHECK(result); return ML_SUCCESS; } /** * @internal */ static tMLError MLDLStandByAccels(struct mldl_cfg *pdata, void *mlsl_handle, unsigned char disable_ax, unsigned char disable_ay, unsigned char disable_az) { unsigned char b; tMLError result; result = MLSLSerialRead(mlsl_handle, pdata->addr, MPUREG_PWR_MGMT_2, 1, &b); ERROR_CHECK(result); b &= ~(BIT_STBY_XA | BIT_STBY_YA | BIT_STBY_ZA); b |= (disable_ax << 2 | disable_ay << 1 | disable_az); result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, MPUREG_PWR_MGMT_2, b); ERROR_CHECK(result); return ML_SUCCESS; } #else /* ! M_HW */ /** * @internal * @brief This function controls the power management on the MPU device. * The entire chip can be put to low power sleep mode, or individual * gyros can be turned on/off. * * Putting the device into sleep mode depending upon the changing needs * of the associated applications is a recommended method for reducing * power consuption. It is a safe opearation in that sleep/wake up of * gyros while running will not result in any interruption of data. * * Although it is entirely allowed to put the device into full sleep * while running the DMP, it is not recomended because it will disrupt * the ongoing calculations carried on inside the DMP and consequently * the sensor fusion algorithm. Furthermore, while in sleep mode * read & write operation from the app processor on both registers and * memory are disabled and can only regained by restoring the MPU in * normal power mode. * Disabling any of the gyro axis will reduce the associated power * consuption from the PLL but will not stop the DMP from running * state. * * @param reset * Non-zero to reset the device. Note that this setting * is volatile and the corresponding register bit will * clear itself right after being applied. * @param sleep * Non-zero to put device into full sleep. * @param disable_gx * Non-zero to disable gyro X. * @param disable_gy * Non-zero to disable gyro Y. * @param disable_gz * Non-zero to disable gyro Z. * * @return ML_SUCCESS if successfull; a non-zero error code otherwise. */ static int MLDLPowerMgmtMPU(struct mldl_cfg *pdata, void *mlsl_handle, unsigned char reset, unsigned char sleep, unsigned char disable_gx, unsigned char disable_gy, unsigned char disable_gz) { unsigned char b; int result; result = MLSLSerialRead(mlsl_handle, pdata->addr, MPUREG_PWR_MGM, 1, &b); ERROR_CHECK(result); /* If we are awake, we need to put it in bypass before resetting */ if ((!(b & BIT_SLEEP)) && reset) result = MLDLSetI2CBypass(pdata, mlsl_handle, 1); /* If we are awake, we need stop the dmp sleeping */ if ((!(b & BIT_SLEEP)) && sleep) dmp_stop(pdata, mlsl_handle); /* Reset if requested */ if (reset) { MPL_LOGV("Reset MPU3050\n"); result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, MPUREG_PWR_MGM, b | BIT_H_RESET); ERROR_CHECK(result); MLOSSleep(5); pdata->gyro_needs_reset = FALSE; /* Some chips are awake after reset and some are asleep, * check the status */ result = MLSLSerialRead(mlsl_handle, pdata->addr, MPUREG_PWR_MGM, 1, &b); ERROR_CHECK(result); } /* Update the suspended state just in case we return early */ if (b & BIT_SLEEP) pdata->gyro_is_suspended = TRUE; else pdata->gyro_is_suspended = FALSE; /* if power status match requested, nothing else's left to do */ if ((b & (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) == (((sleep != 0) * BIT_SLEEP) | ((disable_gx != 0) * BIT_STBY_XG) | ((disable_gy != 0) * BIT_STBY_YG) | ((disable_gz != 0) * BIT_STBY_ZG))) { return ML_SUCCESS; } /* * This specific transition between states needs to be reinterpreted: * (1,1,1,1) -> (0,1,1,1) has to become * (1,1,1,1) -> (1,0,0,0) -> (0,1,1,1) * where * (1,1,1,1) is (sleep=1,disable_gx=1,disable_gy=1,disable_gz=1) */ if ((b & (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) == (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG) && ((!sleep) && disable_gx && disable_gy && disable_gz)) { result = MLDLPowerMgmtMPU(pdata, mlsl_handle, 0, 1, 0, 0, 0); if (result) return result; b |= BIT_SLEEP; b &= ~(BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG); } if ((b & BIT_SLEEP) != ((sleep != 0) * BIT_SLEEP)) { if (sleep) { result = MLDLSetI2CBypass(pdata, mlsl_handle, 1); ERROR_CHECK(result); b |= BIT_SLEEP; result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, MPUREG_PWR_MGM, b); ERROR_CHECK(result); pdata->gyro_is_suspended = TRUE; } else { b &= ~BIT_SLEEP; result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, MPUREG_PWR_MGM, b); ERROR_CHECK(result); pdata->gyro_is_suspended = FALSE; MLOSSleep(5); } } /*--- WORKAROUND FOR PUTTING GYRO AXIS in STAND-BY MODE 1) put one axis at a time in stand-by ---*/ if ((b & BIT_STBY_XG) != ((disable_gx != 0) * BIT_STBY_XG)) { b ^= BIT_STBY_XG; result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, MPUREG_PWR_MGM, b); ERROR_CHECK(result); } if ((b & BIT_STBY_YG) != ((disable_gy != 0) * BIT_STBY_YG)) { b ^= BIT_STBY_YG; result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, MPUREG_PWR_MGM, b); ERROR_CHECK(result); } if ((b & BIT_STBY_ZG) != ((disable_gz != 0) * BIT_STBY_ZG)) { b ^= BIT_STBY_ZG; result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, MPUREG_PWR_MGM, b); ERROR_CHECK(result); } return ML_SUCCESS; } #endif /* M_HW */ void mpu_print_cfg(struct mldl_cfg *mldl_cfg) { struct mpu3050_platform_data *pdata = mldl_cfg->pdata; struct ext_slave_platform_data *accel = &mldl_cfg->pdata->accel; struct ext_slave_platform_data *compass = &mldl_cfg->pdata->compass; struct ext_slave_platform_data *pressure = &mldl_cfg->pdata->pressure; MPL_LOGD("mldl_cfg.addr = %02x\n", mldl_cfg->addr); MPL_LOGD("mldl_cfg.int_config = %02x\n", mldl_cfg->int_config); MPL_LOGD("mldl_cfg.ext_sync = %02x\n", mldl_cfg->ext_sync); MPL_LOGD("mldl_cfg.full_scale = %02x\n", mldl_cfg->full_scale); MPL_LOGD("mldl_cfg.lpf = %02x\n", mldl_cfg->lpf); MPL_LOGD("mldl_cfg.clk_src = %02x\n", mldl_cfg->clk_src); MPL_LOGD("mldl_cfg.divider = %02x\n", mldl_cfg->divider); MPL_LOGD("mldl_cfg.dmp_enable = %02x\n", mldl_cfg->dmp_enable); MPL_LOGD("mldl_cfg.fifo_enable = %02x\n", mldl_cfg->fifo_enable); MPL_LOGD("mldl_cfg.dmp_cfg1 = %02x\n", mldl_cfg->dmp_cfg1); MPL_LOGD("mldl_cfg.dmp_cfg2 = %02x\n", mldl_cfg->dmp_cfg2); MPL_LOGD("mldl_cfg.offset_tc[0] = %02x\n", mldl_cfg->offset_tc[0]); MPL_LOGD("mldl_cfg.offset_tc[1] = %02x\n", mldl_cfg->offset_tc[1]); MPL_LOGD("mldl_cfg.offset_tc[2] = %02x\n", mldl_cfg->offset_tc[2]); MPL_LOGD("mldl_cfg.silicon_revision = %02x\n", mldl_cfg->silicon_revision); MPL_LOGD("mldl_cfg.product_id = %02x\n", mldl_cfg->product_id); MPL_LOGD("mldl_cfg.trim = %02x\n", mldl_cfg->trim); MPL_LOGD("mldl_cfg.requested_sensors= %04lx\n", mldl_cfg->requested_sensors); if (mldl_cfg->accel) { MPL_LOGD("slave_accel->suspend = %02x\n", (int) mldl_cfg->accel->suspend); MPL_LOGD("slave_accel->resume = %02x\n", (int) mldl_cfg->accel->resume); MPL_LOGD("slave_accel->read = %02x\n", (int) mldl_cfg->accel->read); MPL_LOGD("slave_accel->type = %02x\n", mldl_cfg->accel->type); MPL_LOGD("slave_accel->reg = %02x\n", mldl_cfg->accel->reg); MPL_LOGD("slave_accel->len = %02x\n", mldl_cfg->accel->len); MPL_LOGD("slave_accel->endian = %02x\n", mldl_cfg->accel->endian); MPL_LOGD("slave_accel->range.mantissa= %02lx\n", mldl_cfg->accel->range.mantissa); MPL_LOGD("slave_accel->range.fraction= %02lx\n", mldl_cfg->accel->range.fraction); } else { MPL_LOGD("slave_accel = NULL\n"); } if (mldl_cfg->compass) { MPL_LOGD("slave_compass->suspend = %02x\n", (int) mldl_cfg->compass->suspend); MPL_LOGD("slave_compass->resume = %02x\n", (int) mldl_cfg->compass->resume); MPL_LOGD("slave_compass->read = %02x\n", (int) mldl_cfg->compass->read); MPL_LOGD("slave_compass->type = %02x\n", mldl_cfg->compass->type); MPL_LOGD("slave_compass->reg = %02x\n", mldl_cfg->compass->reg); MPL_LOGD("slave_compass->len = %02x\n", mldl_cfg->compass->len); MPL_LOGD("slave_compass->endian = %02x\n", mldl_cfg->compass->endian); MPL_LOGD("slave_compass->range.mantissa= %02lx\n", mldl_cfg->compass->range.mantissa); MPL_LOGD("slave_compass->range.fraction= %02lx\n", mldl_cfg->compass->range.fraction); } else { MPL_LOGD("slave_compass = NULL\n"); } if (mldl_cfg->pressure) { MPL_LOGD("slave_pressure->suspend = %02x\n", (int) mldl_cfg->pressure->suspend); MPL_LOGD("slave_pressure->resume = %02x\n", (int) mldl_cfg->pressure->resume); MPL_LOGD("slave_pressure->read = %02x\n", (int) mldl_cfg->pressure->read); MPL_LOGD("slave_pressure->type = %02x\n", mldl_cfg->pressure->type); MPL_LOGD("slave_pressure->reg = %02x\n", mldl_cfg->pressure->reg); MPL_LOGD("slave_pressure->len = %02x\n", mldl_cfg->pressure->len); MPL_LOGD("slave_pressure->endian = %02x\n", mldl_cfg->pressure->endian); MPL_LOGD("slave_pressure->range.mantissa= %02lx\n", mldl_cfg->pressure->range.mantissa); MPL_LOGD("slave_pressure->range.fraction= %02lx\n", mldl_cfg->pressure->range.fraction); } else { MPL_LOGD("slave_pressure = NULL\n"); } MPL_LOGD("accel->get_slave_descr = %x\n", (unsigned int) accel->get_slave_descr); MPL_LOGD("accel->irq = %02x\n", accel->irq); MPL_LOGD("accel->adapt_num = %02x\n", accel->adapt_num); MPL_LOGD("accel->bus = %02x\n", accel->bus); MPL_LOGD("accel->address = %02x\n", accel->address); MPL_LOGD("accel->orientation =\n" " %2d %2d %2d\n" " %2d %2d %2d\n" " %2d %2d %2d\n", accel->orientation[0], accel->orientation[1], accel->orientation[2], accel->orientation[3], accel->orientation[4], accel->orientation[5], accel->orientation[6], accel->orientation[7], accel->orientation[8]); MPL_LOGD("compass->get_slave_descr = %x\n", (unsigned int) compass->get_slave_descr); MPL_LOGD("compass->irq = %02x\n", compass->irq); MPL_LOGD("compass->adapt_num = %02x\n", compass->adapt_num); MPL_LOGD("compass->bus = %02x\n", compass->bus); MPL_LOGD("compass->address = %02x\n", compass->address); MPL_LOGD("compass->orientation =\n" " %2d %2d %2d\n" " %2d %2d %2d\n" " %2d %2d %2d\n", compass->orientation[0], compass->orientation[1], compass->orientation[2], compass->orientation[3], compass->orientation[4], compass->orientation[5], compass->orientation[6], compass->orientation[7], compass->orientation[8]); MPL_LOGD("pressure->get_slave_descr = %x\n", (unsigned int) pressure->get_slave_descr); MPL_LOGD("pressure->irq = %02x\n", pressure->irq); MPL_LOGD("pressure->adapt_num = %02x\n", pressure->adapt_num); MPL_LOGD("pressure->bus = %02x\n", pressure->bus); MPL_LOGD("pressure->address = %02x\n", pressure->address); MPL_LOGD("pressure->orientation =\n" " %2d %2d %2d\n" " %2d %2d %2d\n" " %2d %2d %2d\n", pressure->orientation[0], pressure->orientation[1], pressure->orientation[2], pressure->orientation[3], pressure->orientation[4], pressure->orientation[5], pressure->orientation[6], pressure->orientation[7], pressure->orientation[8]); MPL_LOGD("pdata->int_config = %02x\n", pdata->int_config); MPL_LOGD("pdata->level_shifter = %02x\n", pdata->level_shifter); MPL_LOGD("pdata->orientation =\n" " %2d %2d %2d\n" " %2d %2d %2d\n" " %2d %2d %2d\n", pdata->orientation[0], pdata->orientation[1], pdata->orientation[2], pdata->orientation[3], pdata->orientation[4], pdata->orientation[5], pdata->orientation[6], pdata->orientation[7], pdata->orientation[8]); MPL_LOGD("Struct sizes: mldl_cfg: %d, " "ext_slave_descr:%d, " "mpu3050_platform_data:%d: RamOffset: %d\n", sizeof(struct mldl_cfg), sizeof(struct ext_slave_descr), sizeof(struct mpu3050_platform_data), offsetof(struct mldl_cfg, ram)); } int mpu_set_slave(struct mldl_cfg *mldl_cfg, void *gyro_handle, struct ext_slave_descr *slave, struct ext_slave_platform_data *slave_pdata) { int result; unsigned char reg; unsigned char slave_reg; unsigned char slave_len; unsigned char slave_endian; unsigned char slave_address; result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, TRUE); if (NULL == slave || NULL == slave_pdata) { slave_reg = 0; slave_len = 0; slave_endian = 0; slave_address = 0; } else { slave_reg = slave->reg; slave_len = slave->len; slave_endian = slave->endian; slave_address = slave_pdata->address; } /* Address */ result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, MPUREG_AUX_SLV_ADDR, slave_address); ERROR_CHECK(result); /* Register */ result = MLSLSerialRead(gyro_handle, mldl_cfg->addr, MPUREG_ACCEL_BURST_ADDR, 1, ®); ERROR_CHECK(result); reg = ((reg & 0x80) | slave_reg); result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, MPUREG_ACCEL_BURST_ADDR, reg); ERROR_CHECK(result); #ifdef M_HW /* Length, byte swapping, grouping & enable */ if (slave_len > BITS_SLV_LENG) { MPL_LOGW("Limiting slave burst read length to " "the allowed maximum (15B, req. %d)\n", slave_len); slave_len = BITS_SLV_LENG; } reg = slave_len; if (slave_endian == EXT_SLAVE_LITTLE_ENDIAN) reg |= BIT_SLV_BYTE_SW; reg |= BIT_SLV_GRP; reg |= BIT_SLV_ENABLE; result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, MPUREG_I2C_SLV0_CTRL, reg); #else /* Length */ result = MLSLSerialRead(gyro_handle, mldl_cfg->addr, MPUREG_USER_CTRL, 1, ®); ERROR_CHECK(result); reg = (reg & ~BIT_AUX_RD_LENG); result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, MPUREG_USER_CTRL, reg); ERROR_CHECK(result); #endif if (slave_address) { result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, FALSE); ERROR_CHECK(result); } return result; } /** * Check to see if the gyro was reset by testing a couple of registers known * to change on reset. * * @param mldl_cfg mldl configuration structure * @param gyro_handle handle used to communicate with the gyro * * @return ML_SUCCESS or non-zero error code */ static int mpu_was_reset(struct mldl_cfg *mldl_cfg, void *gyro_handle) { int result = ML_SUCCESS; unsigned char reg; result = MLSLSerialRead(gyro_handle, mldl_cfg->addr, MPUREG_DMP_CFG_2, 1, ®); ERROR_CHECK(result); if (mldl_cfg->dmp_cfg2 != reg) return TRUE; if (0 != mldl_cfg->dmp_cfg1) return FALSE; result = MLSLSerialRead(gyro_handle, mldl_cfg->addr, MPUREG_SMPLRT_DIV, 1, ®); ERROR_CHECK(result); if (reg != mldl_cfg->divider) return TRUE; if (0 != mldl_cfg->divider) return FALSE; /* Inconclusive assume it was reset */ return TRUE; } static int gyro_resume(struct mldl_cfg *mldl_cfg, void *gyro_handle) { int result; int ii; int jj; unsigned char reg; unsigned char regs[7]; /* Wake up the part */ #ifdef M_HW result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, RESET, WAKE_UP); ERROR_CHECK(result); /* Configure the MPU */ result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, 1); ERROR_CHECK(result); /* setting int_config with the propert flag BIT_BYPASS_EN should be done by the setup functions */ result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, MPUREG_INT_PIN_CFG, (mldl_cfg->pdata->int_config | BIT_BYPASS_EN)); ERROR_CHECK(result); /* temporary: masking out higher bits to avoid switching intelligence */ result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, MPUREG_INT_ENABLE, (mldl_cfg->int_config)); ERROR_CHECK(result); #else result = MLDLPowerMgmtMPU(mldl_cfg, gyro_handle, 0, 0, mldl_cfg->gyro_power & BIT_STBY_XG, mldl_cfg->gyro_power & BIT_STBY_YG, mldl_cfg->gyro_power & BIT_STBY_ZG); if (!mldl_cfg->gyro_needs_reset && !mpu_was_reset(mldl_cfg, gyro_handle)) { return ML_SUCCESS; } result = MLDLPowerMgmtMPU(mldl_cfg, gyro_handle, 1, 0, mldl_cfg->gyro_power & BIT_STBY_XG, mldl_cfg->gyro_power & BIT_STBY_YG, mldl_cfg->gyro_power & BIT_STBY_ZG); ERROR_CHECK(result); result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, MPUREG_INT_CFG, (mldl_cfg->int_config | mldl_cfg->pdata->int_config)); ERROR_CHECK(result); #endif result = MLSLSerialRead(gyro_handle, mldl_cfg->addr, MPUREG_PWR_MGM, 1, ®); ERROR_CHECK(result); reg &= ~BITS_CLKSEL; result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, MPUREG_PWR_MGM, mldl_cfg->clk_src | reg); ERROR_CHECK(result); result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, MPUREG_SMPLRT_DIV, mldl_cfg->divider); ERROR_CHECK(result); #ifdef M_HW reg = DLPF_FS_SYNC_VALUE(0, mldl_cfg->full_scale, 0); result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, MPUREG_GYRO_CONFIG, reg); reg = DLPF_FS_SYNC_VALUE(mldl_cfg->ext_sync, 0, mldl_cfg->lpf); result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, MPUREG_CONFIG, reg); #else reg = DLPF_FS_SYNC_VALUE(mldl_cfg->ext_sync, mldl_cfg->full_scale, mldl_cfg->lpf); result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, MPUREG_DLPF_FS_SYNC, reg); #endif ERROR_CHECK(result); result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, MPUREG_DMP_CFG_1, mldl_cfg->dmp_cfg1); ERROR_CHECK(result); result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, MPUREG_DMP_CFG_2, mldl_cfg->dmp_cfg2); ERROR_CHECK(result); /* Write and verify memory */ for (ii = 0; ii < MPU_MEM_NUM_RAM_BANKS; ii++) { unsigned char read[MPU_MEM_BANK_SIZE]; result = MLSLSerialWriteMem(gyro_handle, mldl_cfg->addr, ((ii << 8) | 0x00), MPU_MEM_BANK_SIZE, mldl_cfg->ram[ii]); ERROR_CHECK(result); result = MLSLSerialReadMem(gyro_handle, mldl_cfg->addr, ((ii << 8) | 0x00), MPU_MEM_BANK_SIZE, read); ERROR_CHECK(result); #ifdef M_HW #define ML_SKIP_CHECK 38 #else #define ML_SKIP_CHECK 20 #endif for (jj = 0; jj < MPU_MEM_BANK_SIZE; jj++) { /* skip the register memory locations */ if (ii == 0 && jj < ML_SKIP_CHECK) continue; if (mldl_cfg->ram[ii][jj] != read[jj]) { result = ML_ERROR_SERIAL_WRITE; break; } } ERROR_CHECK(result); } result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, MPUREG_XG_OFFS_TC, mldl_cfg->offset_tc[0]); ERROR_CHECK(result); result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, MPUREG_YG_OFFS_TC, mldl_cfg->offset_tc[1]); ERROR_CHECK(result); result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, MPUREG_ZG_OFFS_TC, mldl_cfg->offset_tc[2]); ERROR_CHECK(result); regs[0] = MPUREG_X_OFFS_USRH; for (ii = 0; ii < DIM(mldl_cfg->offset); ii++) { regs[1 + ii * 2] = (unsigned char)(mldl_cfg->offset[ii] >> 8) & 0xff; regs[1 + ii * 2 + 1] = (unsigned char)(mldl_cfg->offset[ii] & 0xff); } result = MLSLSerialWrite(gyro_handle, mldl_cfg->addr, 7, regs); ERROR_CHECK(result); /* Configure slaves */ result = MLDLSetLevelShifterBit(mldl_cfg, gyro_handle, mldl_cfg->pdata->level_shifter); ERROR_CHECK(result); return result; } /******************************************************************************* ******************************************************************************* * Exported functions ******************************************************************************* ******************************************************************************/ /** * Initializes the pdata structure to defaults. * * Opens the device to read silicon revision, product id and whoami. * * @param mldl_cfg * The internal device configuration data structure. * @param mlsl_handle * The serial communication handle. * * @return ML_SUCCESS if silicon revision, product id and woami are supported * by this software. */ int mpu3050_open(struct mldl_cfg *mldl_cfg, void *mlsl_handle, void *accel_handle, void *compass_handle, void *pressure_handle) { int result; /* Default is Logic HIGH, pushpull, latch disabled, anyread to clear */ mldl_cfg->int_config = BIT_INT_ANYRD_2CLEAR | BIT_DMP_INT_EN; mldl_cfg->clk_src = MPU_CLK_SEL_PLLGYROZ; mldl_cfg->lpf = MPU_FILTER_42HZ; mldl_cfg->full_scale = MPU_FS_2000DPS; mldl_cfg->divider = 4; mldl_cfg->dmp_enable = 1; mldl_cfg->fifo_enable = 1; mldl_cfg->ext_sync = 0; mldl_cfg->dmp_cfg1 = 0; mldl_cfg->dmp_cfg2 = 0; mldl_cfg->gyro_power = 0; mldl_cfg->gyro_is_bypassed = TRUE; mldl_cfg->dmp_is_running = FALSE; mldl_cfg->gyro_is_suspended = TRUE; mldl_cfg->accel_is_suspended = TRUE; mldl_cfg->compass_is_suspended = TRUE; mldl_cfg->pressure_is_suspended = TRUE; mldl_cfg->gyro_needs_reset = FALSE; if (mldl_cfg->addr == 0) { #ifdef __KERNEL__ return ML_ERROR_INVALID_PARAMETER; #else mldl_cfg->addr = 0x68; #endif } /* * Reset, * Take the DMP out of sleep, and * read the product_id, sillicon rev and whoami */ #ifdef M_HW result = mpu60xx_pwr_mgmt(mldl_cfg, mlsl_handle, RESET, WAKE_UP); #else result = MLDLPowerMgmtMPU(mldl_cfg, mlsl_handle, RESET, 0, 0, 0, 0); #endif ERROR_CHECK(result); result = MLDLGetSiliconRev(mldl_cfg, mlsl_handle); ERROR_CHECK(result); #ifndef M_HW result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr, MPUREG_PRODUCT_ID, 1, &mldl_cfg->product_id); ERROR_CHECK(result); #endif /* Get the factory temperature compensation offsets */ result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr, MPUREG_XG_OFFS_TC, 1, &mldl_cfg->offset_tc[0]); ERROR_CHECK(result); result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr, MPUREG_YG_OFFS_TC, 1, &mldl_cfg->offset_tc[1]); ERROR_CHECK(result); result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr, MPUREG_ZG_OFFS_TC, 1, &mldl_cfg->offset_tc[2]); ERROR_CHECK(result); /* Configure the MPU */ #ifdef M_HW result = mpu60xx_pwr_mgmt(mldl_cfg, mlsl_handle, FALSE, SLEEP); #else result = MLDLPowerMgmtMPU(mldl_cfg, mlsl_handle, 0, SLEEP, 0, 0, 0); #endif ERROR_CHECK(result); if (mldl_cfg->accel && mldl_cfg->accel->init) { result = mldl_cfg->accel->init(accel_handle, mldl_cfg->accel, &mldl_cfg->pdata->accel); ERROR_CHECK(result); } if (mldl_cfg->compass && mldl_cfg->compass->init) { result = mldl_cfg->compass->init(compass_handle, mldl_cfg->compass, &mldl_cfg->pdata->compass); if (ML_SUCCESS != result) { MPL_LOGE("mldl_cfg->compass->init returned %d\n", result); goto out_accel; } } if (mldl_cfg->pressure && mldl_cfg->pressure->init) { result = mldl_cfg->pressure->init(pressure_handle, mldl_cfg->pressure, &mldl_cfg->pdata->pressure); if (ML_SUCCESS != result) { MPL_LOGE("mldl_cfg->pressure->init returned %d\n", result); goto out_compass; } } mldl_cfg->requested_sensors = ML_THREE_AXIS_GYRO; if (mldl_cfg->accel && mldl_cfg->accel->resume) mldl_cfg->requested_sensors |= ML_THREE_AXIS_ACCEL; if (mldl_cfg->compass && mldl_cfg->compass->resume) mldl_cfg->requested_sensors |= ML_THREE_AXIS_COMPASS; if (mldl_cfg->pressure && mldl_cfg->pressure->resume) mldl_cfg->requested_sensors |= ML_THREE_AXIS_PRESSURE; return result; out_compass: if (mldl_cfg->compass->init) mldl_cfg->compass->exit(compass_handle, mldl_cfg->compass, &mldl_cfg->pdata->compass); out_accel: if (mldl_cfg->accel->init) mldl_cfg->accel->exit(accel_handle, mldl_cfg->accel, &mldl_cfg->pdata->accel); return result; } /** * Close the mpu3050 interface * * @param mldl_cfg pointer to the configuration structure * @param mlsl_handle pointer to the serial layer handle * * @return ML_SUCCESS or non-zero error code */ int mpu3050_close(struct mldl_cfg *mldl_cfg, void *mlsl_handle, void *accel_handle, void *compass_handle, void *pressure_handle) { int result = ML_SUCCESS; int ret_result = ML_SUCCESS; if (mldl_cfg->accel && mldl_cfg->accel->exit) { result = mldl_cfg->accel->exit(accel_handle, mldl_cfg->accel, &mldl_cfg->pdata->accel); if (ML_SUCCESS != result) MPL_LOGE("Accel exit failed %d\n", result); ret_result = result; } if (ML_SUCCESS == ret_result) ret_result = result; if (mldl_cfg->compass && mldl_cfg->compass->exit) { result = mldl_cfg->compass->exit(compass_handle, mldl_cfg->compass, &mldl_cfg->pdata->compass); if (ML_SUCCESS != result) MPL_LOGE("Compass exit failed %d\n", result); } if (ML_SUCCESS == ret_result) ret_result = result; if (mldl_cfg->pressure && mldl_cfg->pressure->exit) { result = mldl_cfg->pressure->exit(pressure_handle, mldl_cfg->pressure, &mldl_cfg->pdata->pressure); if (ML_SUCCESS != result) MPL_LOGE("Pressure exit failed %d\n", result); } if (ML_SUCCESS == ret_result) ret_result = result; return ret_result; } /** * @brief resume the MPU3050 device and all the other sensor * devices from their low power state. * * @param mldl_cfg * pointer to the configuration structure * @param gyro_handle * the main file handle to the MPU3050 device. * @param accel_handle * an handle to the accelerometer device, if sitting * onto a separate bus. Can match mlsl_handle if * the accelerometer device operates on the same * primary bus of MPU. * @param compass_handle * an handle to the compass device, if sitting * onto a separate bus. Can match mlsl_handle if * the compass device operates on the same * primary bus of MPU. * @param pressure_handle * an handle to the pressure sensor device, if sitting * onto a separate bus. Can match mlsl_handle if * the pressure sensor device operates on the same * primary bus of MPU. * @param resume_gyro * whether resuming the gyroscope device is * actually needed (if the device supports low power * mode of some sort). * @param resume_accel * whether resuming the accelerometer device is * actually needed (if the device supports low power * mode of some sort). * @param resume_compass * whether resuming the compass device is * actually needed (if the device supports low power * mode of some sort). * @param resume_pressure * whether resuming the pressure sensor device is * actually needed (if the device supports low power * mode of some sort). * @return ML_SUCCESS or a non-zero error code. */ int mpu3050_resume(struct mldl_cfg *mldl_cfg, void *gyro_handle, void *accel_handle, void *compass_handle, void *pressure_handle, bool resume_gyro, bool resume_accel, bool resume_compass, bool resume_pressure) { int result = ML_SUCCESS; #ifdef CONFIG_MPU_SENSORS_DEBUG mpu_print_cfg(mldl_cfg); #endif if (resume_accel && ((!mldl_cfg->accel) || (!mldl_cfg->accel->resume))) return ML_ERROR_INVALID_PARAMETER; if (resume_compass && ((!mldl_cfg->compass) || (!mldl_cfg->compass->resume))) return ML_ERROR_INVALID_PARAMETER; if (resume_pressure && ((!mldl_cfg->pressure) || (!mldl_cfg->pressure->resume))) return ML_ERROR_INVALID_PARAMETER; if (resume_gyro && mldl_cfg->gyro_is_suspended) { result = gyro_resume(mldl_cfg, gyro_handle); ERROR_CHECK(result); } if (resume_accel && mldl_cfg->accel_is_suspended) { if (!mldl_cfg->gyro_is_suspended && EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->accel.bus) { result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, TRUE); ERROR_CHECK(result); } #if 0 result = mldl_cfg->accel->resume(accel_handle, mldl_cfg->accel, &mldl_cfg->pdata->accel); #else result = mpu_accel_resume(mldl_cfg); #endif ERROR_CHECK(result); mldl_cfg->accel_is_suspended = FALSE; } if (!mldl_cfg->gyro_is_suspended && !mldl_cfg->accel_is_suspended && EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->accel.bus) { result = mpu_set_slave(mldl_cfg, gyro_handle, mldl_cfg->accel, &mldl_cfg->pdata->accel); ERROR_CHECK(result); } if (resume_compass && mldl_cfg->compass_is_suspended) { if (!mldl_cfg->gyro_is_suspended && EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->compass.bus) { result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, TRUE); ERROR_CHECK(result); } result = mldl_cfg->compass->resume(compass_handle, mldl_cfg->compass, &mldl_cfg->pdata-> compass); ERROR_CHECK(result); mldl_cfg->compass_is_suspended = FALSE; } if (!mldl_cfg->gyro_is_suspended && !mldl_cfg->compass_is_suspended && EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->compass.bus) { result = mpu_set_slave(mldl_cfg, gyro_handle, mldl_cfg->compass, &mldl_cfg->pdata->compass); ERROR_CHECK(result); } if (resume_pressure && mldl_cfg->pressure_is_suspended) { if (!mldl_cfg->gyro_is_suspended && EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->pressure.bus) { result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, TRUE); ERROR_CHECK(result); } result = mldl_cfg->pressure->resume(pressure_handle, mldl_cfg->pressure, &mldl_cfg->pdata-> pressure); ERROR_CHECK(result); mldl_cfg->pressure_is_suspended = FALSE; } if (!mldl_cfg->gyro_is_suspended && !mldl_cfg->pressure_is_suspended && EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->pressure.bus) { result = mpu_set_slave(mldl_cfg, gyro_handle, mldl_cfg->pressure, &mldl_cfg->pdata->pressure); ERROR_CHECK(result); } /* Now start */ if (resume_gyro) { result = dmp_start(mldl_cfg, gyro_handle); ERROR_CHECK(result); } return result; } /** * @brief suspend the MPU3050 device and all the other sensor * devices into their low power state. * @param gyro_handle * the main file handle to the MPU3050 device. * @param accel_handle * an handle to the accelerometer device, if sitting * onto a separate bus. Can match gyro_handle if * the accelerometer device operates on the same * primary bus of MPU. * @param compass_handle * an handle to the compass device, if sitting * onto a separate bus. Can match gyro_handle if * the compass device operates on the same * primary bus of MPU. * @param pressure_handle * an handle to the pressure sensor device, if sitting * onto a separate bus. Can match gyro_handle if * the pressure sensor device operates on the same * primary bus of MPU. * @param accel * whether suspending the accelerometer device is * actually needed (if the device supports low power * mode of some sort). * @param compass * whether suspending the compass device is * actually needed (if the device supports low power * mode of some sort). * @param pressure * whether suspending the pressure sensor device is * actually needed (if the device supports low power * mode of some sort). * @return ML_SUCCESS or a non-zero error code. */ int mpu3050_suspend(struct mldl_cfg *mldl_cfg, void *gyro_handle, void *accel_handle, void *compass_handle, void *pressure_handle, bool suspend_gyro, bool suspend_accel, bool suspend_compass, bool suspend_pressure) { int result = ML_SUCCESS; if (suspend_gyro && !mldl_cfg->gyro_is_suspended) { #ifdef M_HW return ML_SUCCESS; /* This puts the bus into bypass mode */ result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, 1); ERROR_CHECK(result); result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, 0, SLEEP); #else result = MLDLPowerMgmtMPU(mldl_cfg, gyro_handle, 0, SLEEP, 0, 0, 0); #endif ERROR_CHECK(result); } if (!mldl_cfg->accel_is_suspended && suspend_accel && mldl_cfg->accel && mldl_cfg->accel->suspend) { if (!mldl_cfg->gyro_is_suspended && EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->accel.bus) { result = mpu_set_slave(mldl_cfg, gyro_handle, NULL, NULL); ERROR_CHECK(result); } #if 0 result = mldl_cfg->accel->suspend(accel_handle, mldl_cfg->accel, &mldl_cfg->pdata->accel); #else result = mpu_accel_suspend(mldl_cfg); #endif ERROR_CHECK(result); mldl_cfg->accel_is_suspended = TRUE; } if (!mldl_cfg->compass_is_suspended && suspend_compass && mldl_cfg->compass && mldl_cfg->compass->suspend) { if (!mldl_cfg->gyro_is_suspended && EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->compass.bus) { result = mpu_set_slave(mldl_cfg, gyro_handle, NULL, NULL); ERROR_CHECK(result); } result = mldl_cfg->compass->suspend(compass_handle, mldl_cfg->compass, &mldl_cfg-> pdata->compass); ERROR_CHECK(result); mldl_cfg->compass_is_suspended = TRUE; } if (!mldl_cfg->pressure_is_suspended && suspend_pressure && mldl_cfg->pressure && mldl_cfg->pressure->suspend) { if (!mldl_cfg->gyro_is_suspended && EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->pressure.bus) { result = mpu_set_slave(mldl_cfg, gyro_handle, NULL, NULL); ERROR_CHECK(result); } result = mldl_cfg->pressure->suspend(pressure_handle, mldl_cfg->pressure, &mldl_cfg-> pdata->pressure); ERROR_CHECK(result); mldl_cfg->pressure_is_suspended = TRUE; } return result; } /** * @brief read raw sensor data from the accelerometer device * in use. * @param mldl_cfg * A pointer to the struct mldl_cfg data structure. * @param accel_handle * The handle to the device the accelerometer is connected to. * @param data * a buffer to store the raw sensor data. * @return ML_SUCCESS if successful, a non-zero error code otherwise. */ int mpu3050_read_accel(struct mldl_cfg *mldl_cfg, void *accel_handle, unsigned char *data) { if (NULL != mldl_cfg->accel && NULL != mldl_cfg->accel->read) if ((EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->accel.bus) && (!mldl_cfg->gyro_is_bypassed)) return ML_ERROR_FEATURE_NOT_ENABLED; else return mldl_cfg->accel->read(accel_handle, mldl_cfg->accel, &mldl_cfg->pdata->accel, data); else return ML_ERROR_FEATURE_NOT_IMPLEMENTED; } /** * @brief read raw sensor data from the compass device * in use. * @param mldl_cfg * A pointer to the struct mldl_cfg data structure. * @param compass_handle * The handle to the device the compass is connected to. * @param data * a buffer to store the raw sensor data. * @return ML_SUCCESS if successful, a non-zero error code otherwise. */ int mpu3050_read_compass(struct mldl_cfg *mldl_cfg, void *compass_handle, unsigned char *data) { if (NULL != mldl_cfg->compass && NULL != mldl_cfg->compass->read) if ((EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->compass.bus) && (!mldl_cfg->gyro_is_bypassed)) return ML_ERROR_FEATURE_NOT_ENABLED; else return mldl_cfg->compass->read(compass_handle, mldl_cfg->compass, &mldl_cfg->pdata->compass, data); else return ML_ERROR_FEATURE_NOT_IMPLEMENTED; } /** * @brief read raw sensor data from the pressure device * in use. * @param mldl_cfg * A pointer to the struct mldl_cfg data structure. * @param pressure_handle * The handle to the device the pressure sensor is connected to. * @param data * a buffer to store the raw sensor data. * @return ML_SUCCESS if successful, a non-zero error code otherwise. */ int mpu3050_read_pressure(struct mldl_cfg *mldl_cfg, void *pressure_handle, unsigned char *data) { if (NULL != mldl_cfg->pressure && NULL != mldl_cfg->pressure->read) if ((EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->pressure.bus) && (!mldl_cfg->gyro_is_bypassed)) return ML_ERROR_FEATURE_NOT_ENABLED; else return mldl_cfg->pressure->read( pressure_handle, mldl_cfg->pressure, &mldl_cfg->pdata->pressure, data); else return ML_ERROR_FEATURE_NOT_IMPLEMENTED; } int mpu3050_config_accel(struct mldl_cfg *mldl_cfg, void *accel_handle, struct ext_slave_config *data) { if (NULL != mldl_cfg->accel && NULL != mldl_cfg->accel->config) return mldl_cfg->accel->config(accel_handle, mldl_cfg->accel, &mldl_cfg->pdata->accel, data); else return ML_ERROR_FEATURE_NOT_IMPLEMENTED; } int mpu3050_config_compass(struct mldl_cfg *mldl_cfg, void *compass_handle, struct ext_slave_config *data) { if (NULL != mldl_cfg->compass && NULL != mldl_cfg->compass->config) return mldl_cfg->compass->config(compass_handle, mldl_cfg->compass, &mldl_cfg->pdata->compass, data); else return ML_ERROR_FEATURE_NOT_IMPLEMENTED; } int mpu3050_config_pressure(struct mldl_cfg *mldl_cfg, void *pressure_handle, struct ext_slave_config *data) { if (NULL != mldl_cfg->pressure && NULL != mldl_cfg->pressure->config) return mldl_cfg->pressure->config(pressure_handle, mldl_cfg->pressure, &mldl_cfg->pdata->pressure, data); else return ML_ERROR_FEATURE_NOT_IMPLEMENTED; } int mpu3050_get_config_accel(struct mldl_cfg *mldl_cfg, void *accel_handle, struct ext_slave_config *data) { if (NULL != mldl_cfg->accel && NULL != mldl_cfg->accel->get_config) return mldl_cfg->accel->get_config(accel_handle, mldl_cfg->accel, &mldl_cfg->pdata->accel, data); else return ML_ERROR_FEATURE_NOT_IMPLEMENTED; } int mpu3050_get_config_compass(struct mldl_cfg *mldl_cfg, void *compass_handle, struct ext_slave_config *data) { if (NULL != mldl_cfg->compass && NULL != mldl_cfg->compass->get_config) return mldl_cfg->compass->get_config(compass_handle, mldl_cfg->compass, &mldl_cfg->pdata->compass, data); else return ML_ERROR_FEATURE_NOT_IMPLEMENTED; } int mpu3050_get_config_pressure(struct mldl_cfg *mldl_cfg, void *pressure_handle, struct ext_slave_config *data) { if (NULL != mldl_cfg->pressure && NULL != mldl_cfg->pressure->get_config) return mldl_cfg->pressure->get_config(pressure_handle, mldl_cfg->pressure, &mldl_cfg->pdata->pressure, data); else return ML_ERROR_FEATURE_NOT_IMPLEMENTED; } /** *@} */