diff options
| author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
|---|---|---|
| committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
| commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
| tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/misc/mpu3050 | |
| parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) | |
Diffstat (limited to 'drivers/misc/mpu3050')
| -rw-r--r-- | drivers/misc/mpu3050/Kconfig | 65 | ||||
| -rw-r--r-- | drivers/misc/mpu3050/Makefile | 132 | ||||
| -rw-r--r-- | drivers/misc/mpu3050/accel/kxtf9.c | 669 | ||||
| -rw-r--r-- | drivers/misc/mpu3050/compass/ak8975.c | 258 | ||||
| -rw-r--r-- | drivers/misc/mpu3050/log.h | 306 | ||||
| -rw-r--r-- | drivers/misc/mpu3050/mldl_cfg.c | 1739 | ||||
| -rw-r--r-- | drivers/misc/mpu3050/mldl_cfg.h | 199 | ||||
| -rw-r--r-- | drivers/misc/mpu3050/mlos-kernel.c | 89 | ||||
| -rw-r--r-- | drivers/misc/mpu3050/mlos.h | 73 | ||||
| -rw-r--r-- | drivers/misc/mpu3050/mlsl-kernel.c | 331 | ||||
| -rw-r--r-- | drivers/misc/mpu3050/mlsl.h | 103 | ||||
| -rw-r--r-- | drivers/misc/mpu3050/mltypes.h | 227 | ||||
| -rw-r--r-- | drivers/misc/mpu3050/mpu-dev.c | 1310 | ||||
| -rw-r--r-- | drivers/misc/mpu3050/mpu-i2c.c | 196 | ||||
| -rw-r--r-- | drivers/misc/mpu3050/mpu-i2c.h | 58 | ||||
| -rw-r--r-- | drivers/misc/mpu3050/mpuirq.c | 319 | ||||
| -rw-r--r-- | drivers/misc/mpu3050/mpuirq.h | 42 | ||||
| -rw-r--r-- | drivers/misc/mpu3050/slaveirq.c | 273 | ||||
| -rw-r--r-- | drivers/misc/mpu3050/slaveirq.h | 43 | ||||
| -rw-r--r-- | drivers/misc/mpu3050/timerirq.c | 299 | ||||
| -rw-r--r-- | drivers/misc/mpu3050/timerirq.h | 30 |
21 files changed, 6761 insertions, 0 deletions
diff --git a/drivers/misc/mpu3050/Kconfig b/drivers/misc/mpu3050/Kconfig new file mode 100644 index 00000000000..de240fa0ad8 --- /dev/null +++ b/drivers/misc/mpu3050/Kconfig | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | |||
| 2 | menu "Motion Sensors Support" | ||
| 3 | |||
| 4 | choice | ||
| 5 | tristate "Motion Processing Unit" | ||
| 6 | depends on I2C | ||
| 7 | optional | ||
| 8 | |||
| 9 | config MPU_SENSORS_MPU3050 | ||
| 10 | tristate "MPU3050" | ||
| 11 | help | ||
| 12 | If you say yes here you get support for the MPU3050 Gyroscope driver | ||
| 13 | This driver can also be built as a module. If so, the module | ||
| 14 | will be called mpu3050. | ||
| 15 | |||
| 16 | config MPU_SENSORS_MPU6000 | ||
| 17 | tristate "MPU6000" | ||
| 18 | help | ||
| 19 | If you say yes here you get support for the MPU6000 Gyroscope driver | ||
| 20 | This driver can also be built as a module. If so, the module | ||
| 21 | will be called mpu6000. | ||
| 22 | |||
| 23 | endchoice | ||
| 24 | |||
| 25 | choice | ||
| 26 | prompt "Accelerometer Type" | ||
| 27 | depends on MPU_SENSORS_MPU3050 | ||
| 28 | optional | ||
| 29 | |||
| 30 | config MPU_SENSORS_KXTF9 | ||
| 31 | bool "Kionix KXTF9" | ||
| 32 | help | ||
| 33 | This enables support for the Kionix KXFT9 accelerometer | ||
| 34 | |||
| 35 | endchoice | ||
| 36 | |||
| 37 | choice | ||
| 38 | prompt "Compass Type" | ||
| 39 | depends on MPU_SENSORS_MPU6000 || MPU_SENSORS_MPU3050 | ||
| 40 | optional | ||
| 41 | |||
| 42 | config MPU_SENSORS_AK8975 | ||
| 43 | bool "AKM ak8975" | ||
| 44 | help | ||
| 45 | This enables support for the AKM ak8975 compass | ||
| 46 | |||
| 47 | endchoice | ||
| 48 | |||
| 49 | config MPU_SENSORS_TIMERIRQ | ||
| 50 | tristate "Timer IRQ" | ||
| 51 | help | ||
| 52 | If you say yes here you get access to the timerirq device handle which | ||
| 53 | can be used to select on. This can be used instead of IRQ's, sleeping, | ||
| 54 | or timer threads. Reading from this device returns the same type of | ||
| 55 | information as reading from the MPU and slave IRQ's. | ||
| 56 | |||
| 57 | config MPU_SENSORS_DEBUG | ||
| 58 | bool "MPU debug" | ||
| 59 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6000 || MPU_SENSORS_TIMERIRQ | ||
| 60 | help | ||
| 61 | If you say yes here you get extra debug messages from the MPU3050 | ||
| 62 | and other slave sensors. | ||
| 63 | |||
| 64 | endmenu | ||
| 65 | |||
diff --git a/drivers/misc/mpu3050/Makefile b/drivers/misc/mpu3050/Makefile new file mode 100644 index 00000000000..89ac46fdac5 --- /dev/null +++ b/drivers/misc/mpu3050/Makefile | |||
| @@ -0,0 +1,132 @@ | |||
| 1 | |||
| 2 | # Kernel makefile for motions sensors | ||
| 3 | # | ||
| 4 | # | ||
| 5 | |||
| 6 | # MPU | ||
| 7 | obj-$(CONFIG_MPU_SENSORS_MPU3050) += mpu3050.o | ||
| 8 | mpu3050-objs += mpuirq.o \ | ||
| 9 | slaveirq.o \ | ||
| 10 | mpu-dev.o \ | ||
| 11 | mpu-i2c.o \ | ||
| 12 | mlsl-kernel.o \ | ||
| 13 | mlos-kernel.o \ | ||
| 14 | $(MLLITE_DIR)mldl_cfg.o | ||
| 15 | |||
| 16 | # | ||
| 17 | # Accel options | ||
| 18 | # | ||
| 19 | ifdef CONFIG_MPU_SENSORS_ADXL346 | ||
| 20 | mpu3050-objs += $(MLLITE_DIR)accel/adxl346.o | ||
| 21 | endif | ||
| 22 | |||
| 23 | ifdef CONFIG_MPU_SENSORS_BMA150 | ||
| 24 | mpu3050-objs += $(MLLITE_DIR)accel/bma150.o | ||
| 25 | endif | ||
| 26 | |||
| 27 | ifdef CONFIG_MPU_SENSORS_BMA222 | ||
| 28 | mpu3050-objs += $(MLLITE_DIR)accel/bma222.o | ||
| 29 | endif | ||
| 30 | |||
| 31 | ifdef CONFIG_MPU_SENSORS_KXSD9 | ||
| 32 | mpu3050-objs += $(MLLITE_DIR)accel/kxsd9.o | ||
| 33 | endif | ||
| 34 | |||
| 35 | ifdef CONFIG_MPU_SENSORS_KXTF9 | ||
| 36 | mpu3050-objs += $(MLLITE_DIR)accel/kxtf9.o | ||
| 37 | endif | ||
| 38 | |||
| 39 | ifdef CONFIG_MPU_SENSORS_LIS331DLH | ||
| 40 | mpu3050-objs += $(MLLITE_DIR)accel/lis331.o | ||
| 41 | endif | ||
| 42 | |||
| 43 | ifdef CONFIG_MPU_SENSORS_LIS3DH | ||
| 44 | mpu3050-objs += $(MLLITE_DIR)accel/lis3dh.o | ||
| 45 | endif | ||
| 46 | |||
| 47 | ifdef CONFIG_MPU_SENSORS_LSM303DLHA | ||
| 48 | mpu3050-objs += $(MLLITE_DIR)accel/lsm303a.o | ||
| 49 | endif | ||
| 50 | |||
| 51 | ifdef CONFIG_MPU_SENSORS_MMA8450 | ||
| 52 | mpu3050-objs += $(MLLITE_DIR)accel/mma8450.o | ||
| 53 | endif | ||
| 54 | |||
| 55 | ifdef CONFIG_MPU_SENSORS_MMA845X | ||
| 56 | mpu3050-objs += $(MLLITE_DIR)accel/mma845x.o | ||
| 57 | endif | ||
| 58 | |||
| 59 | # | ||
| 60 | # Compass options | ||
| 61 | # | ||
| 62 | ifdef CONFIG_MPU_SENSORS_AK8975 | ||
| 63 | mpu3050-objs += $(MLLITE_DIR)compass/ak8975.o | ||
| 64 | endif | ||
| 65 | |||
| 66 | ifdef CONFIG_MPU_SENSORS_AMI30X | ||
| 67 | mpu3050-objs += $(MLLITE_DIR)compass/ami30x.o | ||
| 68 | endif | ||
| 69 | |||
| 70 | ifdef CONFIG_MPU_SENSORS_AMI306 | ||
| 71 | mpu3050-objs += $(MLLITE_DIR)compass/ami306.o | ||
| 72 | endif | ||
| 73 | |||
| 74 | ifdef CONFIG_MPU_SENSORS_HMC5883 | ||
| 75 | mpu3050-objs += $(MLLITE_DIR)compass/hmc5883.o | ||
| 76 | endif | ||
| 77 | |||
| 78 | ifdef CONFIG_MPU_SENSORS_LSM303DLHM | ||
| 79 | mpu3050-objs += $(MLLITE_DIR)compass/lsm303m.o | ||
| 80 | endif | ||
| 81 | |||
| 82 | ifdef CONFIG_MPU_SENSORS_MMC314X | ||
| 83 | mpu3050-objs += $(MLLITE_DIR)compass/mmc314x.o | ||
| 84 | endif | ||
| 85 | |||
| 86 | ifdef CONFIG_MPU_SENSORS_YAS529 | ||
| 87 | mpu3050-objs += $(MLLITE_DIR)compass/yas529-kernel.o | ||
| 88 | endif | ||
| 89 | |||
| 90 | ifdef CONFIG_MPU_SENSORS_YAS530 | ||
| 91 | mpu3050-objs += $(MLLITE_DIR)compass/yas530.o | ||
| 92 | endif | ||
| 93 | |||
| 94 | ifdef CONFIG_MPU_SENSORS_HSCDTD002B | ||
| 95 | mpu3050-objs += $(MLLITE_DIR)compass/hscdtd002b.o | ||
| 96 | endif | ||
| 97 | |||
| 98 | ifdef CONFIG_MPU_SENSORS_HSCDTD004A | ||
| 99 | mpu3050-objs += $(MLLITE_DIR)compass/hscdtd004a.o | ||
| 100 | endif | ||
| 101 | # | ||
| 102 | # Pressure options | ||
| 103 | # | ||
| 104 | ifdef CONFIG_MPU_SENSORS_BMA085 | ||
| 105 | mpu3050-objs += $(MLLITE_DIR)pressure/bma085.o | ||
| 106 | endif | ||
| 107 | |||
| 108 | EXTRA_CFLAGS += -I$(M)/$(MLLITE_DIR) \ | ||
| 109 | -I$(M)/../../include \ | ||
| 110 | -Idrivers/misc/mpu3050 \ | ||
| 111 | -Iinclude/linux | ||
| 112 | |||
| 113 | obj-$(CONFIG_MPU_SENSORS_MPU6000)+= mpu6000.o | ||
| 114 | mpu6000-objs += mpuirq.o \ | ||
| 115 | slaveirq.o \ | ||
| 116 | mpu-dev.o \ | ||
| 117 | mpu-i2c.o \ | ||
| 118 | mlsl-kernel.o \ | ||
| 119 | mlos-kernel.o \ | ||
| 120 | $(MLLITE_DIR)mldl_cfg.o \ | ||
| 121 | $(MLLITE_DIR)accel/mantis.o | ||
| 122 | |||
| 123 | ifdef CONFIG_MPU_SENSORS_AK8975 | ||
| 124 | mpu6000-objs += $(MLLITE_DIR)compass/ak8975.o | ||
| 125 | endif | ||
| 126 | |||
| 127 | ifdef CONFIG_MPU_SENSORS_MPU6000 | ||
| 128 | EXTRA_CFLAGS += -DM_HW | ||
| 129 | endif | ||
| 130 | |||
| 131 | obj-$(CONFIG_MPU_SENSORS_TIMERIRQ)+= timerirq.o | ||
| 132 | |||
diff --git a/drivers/misc/mpu3050/accel/kxtf9.c b/drivers/misc/mpu3050/accel/kxtf9.c new file mode 100644 index 00000000000..938cd572a8f --- /dev/null +++ b/drivers/misc/mpu3050/accel/kxtf9.c | |||
| @@ -0,0 +1,669 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2010 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @defgroup ACCELDL (Motion Library - Accelerometer Driver Layer) | ||
| 22 | * @brief Provides the interface to setup and handle an accelerometers | ||
| 23 | * connected to the secondary I2C interface of the gyroscope. | ||
| 24 | * | ||
| 25 | * @{ | ||
| 26 | * @file kxtf9.c | ||
| 27 | * @brief Accelerometer setup and handling methods. | ||
| 28 | */ | ||
| 29 | |||
| 30 | /* ------------------ */ | ||
| 31 | /* - Include Files. - */ | ||
| 32 | /* ------------------ */ | ||
| 33 | |||
| 34 | #undef MPL_LOG_NDEBUG | ||
| 35 | #define MPL_LOG_NDEBUG 1 | ||
| 36 | |||
| 37 | #ifdef __KERNEL__ | ||
| 38 | #include <linux/module.h> | ||
| 39 | #endif | ||
| 40 | |||
| 41 | #include "mpu.h" | ||
| 42 | #include "mlsl.h" | ||
| 43 | #include "mlos.h" | ||
| 44 | |||
| 45 | #include <log.h> | ||
| 46 | #undef MPL_LOG_TAG | ||
| 47 | #define MPL_LOG_TAG "MPL-acc" | ||
| 48 | |||
| 49 | #define KXTF9_XOUT_HPF_L (0x00) /* 0000 0000 */ | ||
| 50 | #define KXTF9_XOUT_HPF_H (0x01) /* 0000 0001 */ | ||
| 51 | #define KXTF9_YOUT_HPF_L (0x02) /* 0000 0010 */ | ||
| 52 | #define KXTF9_YOUT_HPF_H (0x03) /* 0000 0011 */ | ||
| 53 | #define KXTF9_ZOUT_HPF_L (0x04) /* 0001 0100 */ | ||
| 54 | #define KXTF9_ZOUT_HPF_H (0x05) /* 0001 0101 */ | ||
| 55 | #define KXTF9_XOUT_L (0x06) /* 0000 0110 */ | ||
| 56 | #define KXTF9_XOUT_H (0x07) /* 0000 0111 */ | ||
| 57 | #define KXTF9_YOUT_L (0x08) /* 0000 1000 */ | ||
| 58 | #define KXTF9_YOUT_H (0x09) /* 0000 1001 */ | ||
| 59 | #define KXTF9_ZOUT_L (0x0A) /* 0001 1010 */ | ||
| 60 | #define KXTF9_ZOUT_H (0x0B) /* 0001 1011 */ | ||
| 61 | #define KXTF9_ST_RESP (0x0C) /* 0000 1100 */ | ||
| 62 | #define KXTF9_WHO_AM_I (0x0F) /* 0000 1111 */ | ||
| 63 | #define KXTF9_TILT_POS_CUR (0x10) /* 0001 0000 */ | ||
| 64 | #define KXTF9_TILT_POS_PRE (0x11) /* 0001 0001 */ | ||
| 65 | #define KXTF9_INT_SRC_REG1 (0x15) /* 0001 0101 */ | ||
| 66 | #define KXTF9_INT_SRC_REG2 (0x16) /* 0001 0110 */ | ||
| 67 | #define KXTF9_STATUS_REG (0x18) /* 0001 1000 */ | ||
| 68 | #define KXTF9_INT_REL (0x1A) /* 0001 1010 */ | ||
| 69 | #define KXTF9_CTRL_REG1 (0x1B) /* 0001 1011 */ | ||
| 70 | #define KXTF9_CTRL_REG2 (0x1C) /* 0001 1100 */ | ||
| 71 | #define KXTF9_CTRL_REG3 (0x1D) /* 0001 1101 */ | ||
| 72 | #define KXTF9_INT_CTRL_REG1 (0x1E) /* 0001 1110 */ | ||
| 73 | #define KXTF9_INT_CTRL_REG2 (0x1F) /* 0001 1111 */ | ||
| 74 | #define KXTF9_INT_CTRL_REG3 (0x20) /* 0010 0000 */ | ||
| 75 | #define KXTF9_DATA_CTRL_REG (0x21) /* 0010 0001 */ | ||
| 76 | #define KXTF9_TILT_TIMER (0x28) /* 0010 1000 */ | ||
| 77 | #define KXTF9_WUF_TIMER (0x29) /* 0010 1001 */ | ||
| 78 | #define KXTF9_TDT_TIMER (0x2B) /* 0010 1011 */ | ||
| 79 | #define KXTF9_TDT_H_THRESH (0x2C) /* 0010 1100 */ | ||
| 80 | #define KXTF9_TDT_L_THRESH (0x2D) /* 0010 1101 */ | ||
| 81 | #define KXTF9_TDT_TAP_TIMER (0x2E) /* 0010 1110 */ | ||
| 82 | #define KXTF9_TDT_TOTAL_TIMER (0x2F) /* 0010 1111 */ | ||
| 83 | #define KXTF9_TDT_LATENCY_TIMER (0x30) /* 0011 0000 */ | ||
| 84 | #define KXTF9_TDT_WINDOW_TIMER (0x31) /* 0011 0001 */ | ||
| 85 | #define KXTF9_WUF_THRESH (0x5A) /* 0101 1010 */ | ||
| 86 | #define KXTF9_TILT_ANGLE (0x5C) /* 0101 1100 */ | ||
| 87 | #define KXTF9_HYST_SET (0x5F) /* 0101 1111 */ | ||
| 88 | |||
| 89 | #define KXTF9_MAX_DUR (0xFF) | ||
| 90 | #define KXTF9_MAX_THS (0xFF) | ||
| 91 | #define KXTF9_THS_COUNTS_P_G (32) | ||
| 92 | |||
| 93 | /* --------------------- */ | ||
| 94 | /* - Variables. - */ | ||
| 95 | /* --------------------- */ | ||
| 96 | |||
| 97 | struct kxtf9_config { | ||
| 98 | unsigned int odr; /* Output data rate mHz */ | ||
| 99 | unsigned int fsr; /* full scale range mg */ | ||
| 100 | unsigned int ths; /* Motion no-motion thseshold mg */ | ||
| 101 | unsigned int dur; /* Motion no-motion duration ms */ | ||
| 102 | unsigned int irq_type; | ||
| 103 | unsigned char reg_ths; | ||
| 104 | unsigned char reg_dur; | ||
| 105 | unsigned char reg_odr; | ||
| 106 | unsigned char reg_int_cfg1; | ||
| 107 | unsigned char reg_int_cfg2; | ||
| 108 | unsigned char ctrl_reg1; | ||
| 109 | }; | ||
| 110 | |||
| 111 | struct kxtf9_private_data { | ||
| 112 | struct kxtf9_config suspend; | ||
| 113 | struct kxtf9_config resume; | ||
| 114 | }; | ||
| 115 | |||
| 116 | /***************************************** | ||
| 117 | Accelerometer Initialization Functions | ||
| 118 | *****************************************/ | ||
| 119 | |||
| 120 | static int kxtf9_set_ths(void *mlsl_handle, | ||
| 121 | struct ext_slave_platform_data *pdata, | ||
| 122 | struct kxtf9_config *config, | ||
| 123 | int apply, | ||
| 124 | long ths) | ||
| 125 | { | ||
| 126 | int result = ML_SUCCESS; | ||
| 127 | if ((ths * KXTF9_THS_COUNTS_P_G / 1000) > KXTF9_MAX_THS) | ||
| 128 | ths = (KXTF9_MAX_THS * 1000) / KXTF9_THS_COUNTS_P_G; | ||
| 129 | |||
| 130 | if (ths < 0) | ||
| 131 | ths = 0; | ||
| 132 | |||
| 133 | config->ths = ths; | ||
| 134 | config->reg_ths = (unsigned char) | ||
| 135 | ((long)(ths * KXTF9_THS_COUNTS_P_G) / 1000); | ||
| 136 | MPL_LOGV("THS: %d, 0x%02x\n", config->ths, (int)config->reg_ths); | ||
| 137 | if (apply) | ||
| 138 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 139 | KXTF9_WUF_THRESH, | ||
| 140 | config->reg_ths); | ||
| 141 | return result; | ||
| 142 | } | ||
| 143 | |||
| 144 | static int kxtf9_set_dur(void *mlsl_handle, | ||
| 145 | struct ext_slave_platform_data *pdata, | ||
| 146 | struct kxtf9_config *config, | ||
| 147 | int apply, | ||
| 148 | long dur) | ||
| 149 | { | ||
| 150 | int result = ML_SUCCESS; | ||
| 151 | long reg_dur = (dur * config->odr) / 1000000; | ||
| 152 | config->dur = dur; | ||
| 153 | |||
| 154 | if (reg_dur > KXTF9_MAX_DUR) | ||
| 155 | reg_dur = KXTF9_MAX_DUR; | ||
| 156 | |||
| 157 | config->reg_dur = (unsigned char) reg_dur; | ||
| 158 | MPL_LOGV("DUR: %d, 0x%02x\n", config->dur, (int)config->reg_dur); | ||
| 159 | if (apply) | ||
| 160 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 161 | KXTF9_WUF_TIMER, | ||
| 162 | (unsigned char)reg_dur); | ||
| 163 | return result; | ||
| 164 | } | ||
| 165 | |||
| 166 | /** | ||
| 167 | * Sets the IRQ to fire when one of the IRQ events occur. Threshold and | ||
| 168 | * duration will not be used uless the type is MOT or NMOT. | ||
| 169 | * | ||
| 170 | * @param config configuration to apply to, suspend or resume | ||
| 171 | * @param irq_type The type of IRQ. Valid values are | ||
| 172 | * - MPU_SLAVE_IRQ_TYPE_NONE | ||
| 173 | * - MPU_SLAVE_IRQ_TYPE_MOTION | ||
| 174 | * - MPU_SLAVE_IRQ_TYPE_DATA_READY | ||
| 175 | */ | ||
| 176 | static int kxtf9_set_irq(void *mlsl_handle, | ||
| 177 | struct ext_slave_platform_data *pdata, | ||
| 178 | struct kxtf9_config *config, | ||
| 179 | int apply, | ||
| 180 | long irq_type) | ||
| 181 | { | ||
| 182 | int result = ML_SUCCESS; | ||
| 183 | struct kxtf9_private_data *private_data = pdata->private_data; | ||
| 184 | |||
| 185 | config->irq_type = (unsigned char)irq_type; | ||
| 186 | config->ctrl_reg1 &= ~0x22; | ||
| 187 | if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
| 188 | config->ctrl_reg1 |= 0x20; | ||
| 189 | config->reg_int_cfg1 = 0x38; | ||
| 190 | config->reg_int_cfg2 = 0x00; | ||
| 191 | } else if (irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) { | ||
| 192 | config->ctrl_reg1 |= 0x02; | ||
| 193 | if ((unsigned long) config == | ||
| 194 | (unsigned long) &private_data->suspend) | ||
| 195 | config->reg_int_cfg1 = 0x34; | ||
| 196 | else | ||
| 197 | config->reg_int_cfg1 = 0x24; | ||
| 198 | config->reg_int_cfg2 = 0xE0; | ||
| 199 | } else { | ||
| 200 | config->reg_int_cfg1 = 0x00; | ||
| 201 | config->reg_int_cfg2 = 0x00; | ||
| 202 | } | ||
| 203 | |||
| 204 | if (apply) { | ||
| 205 | /* Must clear bit 7 before writing new configuration */ | ||
| 206 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 207 | KXTF9_CTRL_REG1, 0x40); | ||
| 208 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 209 | KXTF9_INT_CTRL_REG1, | ||
| 210 | config->reg_int_cfg1); | ||
| 211 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 212 | KXTF9_INT_CTRL_REG2, | ||
| 213 | config->reg_int_cfg2); | ||
| 214 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 215 | KXTF9_CTRL_REG1, | ||
| 216 | config->ctrl_reg1); | ||
| 217 | } | ||
| 218 | MPL_LOGV("CTRL_REG1: %lx, INT_CFG1: %lx, INT_CFG2: %lx\n", | ||
| 219 | (unsigned long)config->ctrl_reg1, | ||
| 220 | (unsigned long)config->reg_int_cfg1, | ||
| 221 | (unsigned long)config->reg_int_cfg2); | ||
| 222 | |||
| 223 | return result; | ||
| 224 | } | ||
| 225 | |||
| 226 | /** | ||
| 227 | * Set the Output data rate for the particular configuration | ||
| 228 | * | ||
| 229 | * @param config Config to modify with new ODR | ||
| 230 | * @param odr Output data rate in units of 1/1000Hz | ||
| 231 | */ | ||
| 232 | static int kxtf9_set_odr(void *mlsl_handle, | ||
| 233 | struct ext_slave_platform_data *pdata, | ||
| 234 | struct kxtf9_config *config, | ||
| 235 | int apply, | ||
| 236 | long odr) | ||
| 237 | { | ||
| 238 | unsigned char bits; | ||
| 239 | int result = ML_SUCCESS; | ||
| 240 | |||
| 241 | /* Data sheet says there is 12.5 hz, but that seems to produce a single | ||
| 242 | * correct data value, thus we remove it from the table */ | ||
| 243 | if (odr > 400000) { | ||
| 244 | config->odr = 800000; | ||
| 245 | bits = 0x06; | ||
| 246 | } else if (odr > 200000) { | ||
| 247 | config->odr = 400000; | ||
| 248 | bits = 0x05; | ||
| 249 | } else if (odr > 100000) { | ||
| 250 | config->odr = 200000; | ||
| 251 | bits = 0x04; | ||
| 252 | } else if (odr > 50000) { | ||
| 253 | config->odr = 100000; | ||
| 254 | bits = 0x03; | ||
| 255 | } else if (odr > 25000) { | ||
| 256 | config->odr = 50000; | ||
| 257 | bits = 0x02; | ||
| 258 | } else if (odr != 0) { | ||
| 259 | config->odr = 25000; | ||
| 260 | bits = 0x01; | ||
| 261 | } else { | ||
| 262 | config->odr = 0; | ||
| 263 | bits = 0; | ||
| 264 | } | ||
| 265 | |||
| 266 | config->reg_odr = bits; | ||
| 267 | kxtf9_set_dur(mlsl_handle, pdata, | ||
| 268 | config, apply, config->dur); | ||
| 269 | MPL_LOGV("ODR: %d, 0x%02x\n", config->odr, (int)config->ctrl_reg1); | ||
| 270 | if (apply) { | ||
| 271 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 272 | KXTF9_DATA_CTRL_REG, | ||
| 273 | config->reg_odr); | ||
| 274 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 275 | KXTF9_CTRL_REG1, | ||
| 276 | 0x40); | ||
| 277 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 278 | KXTF9_CTRL_REG1, | ||
| 279 | config->ctrl_reg1); | ||
| 280 | } | ||
| 281 | return result; | ||
| 282 | } | ||
| 283 | |||
| 284 | /** | ||
| 285 | * Set the full scale range of the accels | ||
| 286 | * | ||
| 287 | * @param config pointer to configuration | ||
| 288 | * @param fsr requested full scale range | ||
| 289 | */ | ||
| 290 | static int kxtf9_set_fsr(void *mlsl_handle, | ||
| 291 | struct ext_slave_platform_data *pdata, | ||
| 292 | struct kxtf9_config *config, | ||
| 293 | int apply, | ||
| 294 | long fsr) | ||
| 295 | { | ||
| 296 | int result = ML_SUCCESS; | ||
| 297 | |||
| 298 | config->ctrl_reg1 = (config->ctrl_reg1 & 0xE7); | ||
| 299 | if (fsr <= 2000) { | ||
| 300 | config->fsr = 2000; | ||
| 301 | config->ctrl_reg1 |= 0x00; | ||
| 302 | } else if (fsr <= 4000) { | ||
| 303 | config->fsr = 4000; | ||
| 304 | config->ctrl_reg1 |= 0x08; | ||
| 305 | } else { | ||
| 306 | config->fsr = 8000; | ||
| 307 | config->ctrl_reg1 |= 0x10; | ||
| 308 | } | ||
| 309 | |||
| 310 | MPL_LOGV("FSR: %d\n", config->fsr); | ||
| 311 | if (apply) { | ||
| 312 | /* Must clear bit 7 before writing new configuration */ | ||
| 313 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 314 | KXTF9_CTRL_REG1, 0x40); | ||
| 315 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 316 | KXTF9_CTRL_REG1, config->ctrl_reg1); | ||
| 317 | } | ||
| 318 | return result; | ||
| 319 | } | ||
| 320 | |||
| 321 | static int kxtf9_suspend(void *mlsl_handle, | ||
| 322 | struct ext_slave_descr *slave, | ||
| 323 | struct ext_slave_platform_data *pdata) | ||
| 324 | { | ||
| 325 | int result; | ||
| 326 | unsigned char data; | ||
| 327 | struct kxtf9_private_data *private_data = pdata->private_data; | ||
| 328 | |||
| 329 | /* Wake up */ | ||
| 330 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 331 | KXTF9_CTRL_REG1, 0x40); | ||
| 332 | ERROR_CHECK(result); | ||
| 333 | /* INT_CTRL_REG1: */ | ||
| 334 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 335 | KXTF9_INT_CTRL_REG1, | ||
| 336 | private_data->suspend.reg_int_cfg1); | ||
| 337 | ERROR_CHECK(result); | ||
| 338 | /* WUF_THRESH: */ | ||
| 339 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 340 | KXTF9_WUF_THRESH, | ||
| 341 | private_data->suspend.reg_ths); | ||
| 342 | ERROR_CHECK(result); | ||
| 343 | /* DATA_CTRL_REG */ | ||
| 344 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 345 | KXTF9_DATA_CTRL_REG, | ||
| 346 | private_data->suspend.reg_odr); | ||
| 347 | ERROR_CHECK(result); | ||
| 348 | /* WUF_TIMER */ | ||
| 349 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 350 | KXTF9_WUF_TIMER, private_data->suspend.reg_dur); | ||
| 351 | ERROR_CHECK(result); | ||
| 352 | |||
| 353 | /* Normal operation */ | ||
| 354 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 355 | KXTF9_CTRL_REG1, | ||
| 356 | private_data->suspend.ctrl_reg1); | ||
| 357 | ERROR_CHECK(result); | ||
| 358 | result = MLSLSerialRead(mlsl_handle, pdata->address, | ||
| 359 | KXTF9_INT_REL, 1, &data); | ||
| 360 | ERROR_CHECK(result); | ||
| 361 | |||
| 362 | return result; | ||
| 363 | } | ||
| 364 | |||
| 365 | /* full scale setting - register and mask */ | ||
| 366 | #define ACCEL_KIONIX_CTRL_REG (0x1b) | ||
| 367 | #define ACCEL_KIONIX_CTRL_MASK (0x18) | ||
| 368 | |||
| 369 | static int kxtf9_resume(void *mlsl_handle, | ||
| 370 | struct ext_slave_descr *slave, | ||
| 371 | struct ext_slave_platform_data *pdata) | ||
| 372 | { | ||
| 373 | int result = ML_SUCCESS; | ||
| 374 | unsigned char data; | ||
| 375 | struct kxtf9_private_data *private_data = pdata->private_data; | ||
| 376 | |||
| 377 | /* Wake up */ | ||
| 378 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 379 | KXTF9_CTRL_REG1, 0x40); | ||
| 380 | ERROR_CHECK(result); | ||
| 381 | /* INT_CTRL_REG1: */ | ||
| 382 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 383 | KXTF9_INT_CTRL_REG1, | ||
| 384 | private_data->resume.reg_int_cfg1); | ||
| 385 | ERROR_CHECK(result); | ||
| 386 | /* WUF_THRESH: */ | ||
| 387 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 388 | KXTF9_WUF_THRESH, private_data->resume.reg_ths); | ||
| 389 | ERROR_CHECK(result); | ||
| 390 | /* DATA_CTRL_REG */ | ||
| 391 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 392 | KXTF9_DATA_CTRL_REG, | ||
| 393 | private_data->resume.reg_odr); | ||
| 394 | ERROR_CHECK(result); | ||
| 395 | /* WUF_TIMER */ | ||
| 396 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 397 | KXTF9_WUF_TIMER, private_data->resume.reg_dur); | ||
| 398 | ERROR_CHECK(result); | ||
| 399 | |||
| 400 | /* Normal operation */ | ||
| 401 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 402 | KXTF9_CTRL_REG1, | ||
| 403 | private_data->resume.ctrl_reg1); | ||
| 404 | ERROR_CHECK(result); | ||
| 405 | result = MLSLSerialRead(mlsl_handle, pdata->address, | ||
| 406 | KXTF9_INT_REL, 1, &data); | ||
| 407 | ERROR_CHECK(result); | ||
| 408 | |||
| 409 | return ML_SUCCESS; | ||
| 410 | } | ||
| 411 | |||
| 412 | static int kxtf9_init(void *mlsl_handle, | ||
| 413 | struct ext_slave_descr *slave, | ||
| 414 | struct ext_slave_platform_data *pdata) | ||
| 415 | { | ||
| 416 | |||
| 417 | struct kxtf9_private_data *private_data; | ||
| 418 | int result = ML_SUCCESS; | ||
| 419 | |||
| 420 | private_data = (struct kxtf9_private_data *) | ||
| 421 | MLOSMalloc(sizeof(struct kxtf9_private_data)); | ||
| 422 | |||
| 423 | if (!private_data) | ||
| 424 | return ML_ERROR_MEMORY_EXAUSTED; | ||
| 425 | |||
| 426 | /* RAM reset */ | ||
| 427 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 428 | KXTF9_CTRL_REG1, | ||
| 429 | 0x40); /* Fastest Reset */ | ||
| 430 | ERROR_CHECK(result); | ||
| 431 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 432 | KXTF9_DATA_CTRL_REG, | ||
| 433 | 0x36); /* Fastest Reset */ | ||
| 434 | ERROR_CHECK(result); | ||
| 435 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 436 | KXTF9_CTRL_REG3, 0xcd); /* Reset */ | ||
| 437 | ERROR_CHECK(result); | ||
| 438 | MLOSSleep(2); | ||
| 439 | |||
| 440 | pdata->private_data = private_data; | ||
| 441 | |||
| 442 | private_data->resume.ctrl_reg1 = 0xC0; | ||
| 443 | private_data->suspend.ctrl_reg1 = 0x40; | ||
| 444 | |||
| 445 | result = kxtf9_set_dur(mlsl_handle, pdata, &private_data->suspend, | ||
| 446 | FALSE, 1000); | ||
| 447 | ERROR_CHECK(result); | ||
| 448 | result = kxtf9_set_dur(mlsl_handle, pdata, &private_data->resume, | ||
| 449 | FALSE, 2540); | ||
| 450 | ERROR_CHECK(result); | ||
| 451 | |||
| 452 | result = kxtf9_set_odr(mlsl_handle, pdata, &private_data->suspend, | ||
| 453 | FALSE, 50000); | ||
| 454 | ERROR_CHECK(result); | ||
| 455 | result = kxtf9_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
| 456 | FALSE, 200000); | ||
| 457 | |||
| 458 | result = kxtf9_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
| 459 | FALSE, 2000); | ||
| 460 | ERROR_CHECK(result); | ||
| 461 | result = kxtf9_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
| 462 | FALSE, 2000); | ||
| 463 | ERROR_CHECK(result); | ||
| 464 | |||
| 465 | result = kxtf9_set_ths(mlsl_handle, pdata, &private_data->suspend, | ||
| 466 | FALSE, 80); | ||
| 467 | ERROR_CHECK(result); | ||
| 468 | result = kxtf9_set_ths(mlsl_handle, pdata, &private_data->resume, | ||
| 469 | FALSE, 40); | ||
| 470 | ERROR_CHECK(result); | ||
| 471 | |||
| 472 | result = kxtf9_set_irq(mlsl_handle, pdata, &private_data->suspend, | ||
| 473 | FALSE, | ||
| 474 | MPU_SLAVE_IRQ_TYPE_NONE); | ||
| 475 | ERROR_CHECK(result); | ||
| 476 | result = kxtf9_set_irq(mlsl_handle, pdata, &private_data->resume, | ||
| 477 | FALSE, | ||
| 478 | MPU_SLAVE_IRQ_TYPE_NONE); | ||
| 479 | ERROR_CHECK(result); | ||
| 480 | return result; | ||
| 481 | } | ||
| 482 | |||
| 483 | static int kxtf9_exit(void *mlsl_handle, | ||
| 484 | struct ext_slave_descr *slave, | ||
| 485 | struct ext_slave_platform_data *pdata) | ||
| 486 | { | ||
| 487 | if (pdata->private_data) | ||
| 488 | return MLOSFree(pdata->private_data); | ||
| 489 | else | ||
| 490 | return ML_SUCCESS; | ||
| 491 | } | ||
| 492 | |||
| 493 | static int kxtf9_config(void *mlsl_handle, | ||
| 494 | struct ext_slave_descr *slave, | ||
| 495 | struct ext_slave_platform_data *pdata, | ||
| 496 | struct ext_slave_config *data) | ||
| 497 | { | ||
| 498 | int retval; | ||
| 499 | long odr; | ||
| 500 | struct kxtf9_private_data *private_data = pdata->private_data; | ||
| 501 | if (!data->data) | ||
| 502 | return ML_ERROR_INVALID_PARAMETER; | ||
| 503 | |||
| 504 | switch (data->key) { | ||
| 505 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 506 | return kxtf9_set_odr(mlsl_handle, pdata, | ||
| 507 | &private_data->suspend, | ||
| 508 | data->apply, | ||
| 509 | *((long *)data->data)); | ||
| 510 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 511 | odr = *((long *)data->data); | ||
| 512 | if (odr != 0) | ||
| 513 | private_data->resume.ctrl_reg1 |= 0x80; | ||
| 514 | |||
| 515 | retval = kxtf9_set_odr(mlsl_handle, pdata, | ||
| 516 | &private_data->resume, | ||
| 517 | data->apply, | ||
| 518 | odr); | ||
| 519 | return retval; | ||
| 520 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 521 | return kxtf9_set_fsr(mlsl_handle, pdata, | ||
| 522 | &private_data->suspend, | ||
| 523 | data->apply, | ||
| 524 | *((long *)data->data)); | ||
| 525 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 526 | return kxtf9_set_fsr(mlsl_handle, pdata, | ||
| 527 | &private_data->resume, | ||
| 528 | data->apply, | ||
| 529 | *((long *)data->data)); | ||
| 530 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 531 | return kxtf9_set_ths(mlsl_handle, pdata, | ||
| 532 | &private_data->suspend, | ||
| 533 | data->apply, | ||
| 534 | *((long *)data->data)); | ||
| 535 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 536 | return kxtf9_set_ths(mlsl_handle, pdata, | ||
| 537 | &private_data->resume, | ||
| 538 | data->apply, | ||
| 539 | *((long *)data->data)); | ||
| 540 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 541 | return kxtf9_set_dur(mlsl_handle, pdata, | ||
| 542 | &private_data->suspend, | ||
| 543 | data->apply, | ||
| 544 | *((long *)data->data)); | ||
| 545 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 546 | return kxtf9_set_dur(mlsl_handle, pdata, | ||
| 547 | &private_data->resume, | ||
| 548 | data->apply, | ||
| 549 | *((long *)data->data)); | ||
| 550 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 551 | return kxtf9_set_irq(mlsl_handle, pdata, | ||
| 552 | &private_data->suspend, | ||
| 553 | data->apply, | ||
| 554 | *((long *)data->data)); | ||
| 555 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 556 | return kxtf9_set_irq(mlsl_handle, pdata, | ||
| 557 | &private_data->resume, | ||
| 558 | data->apply, | ||
| 559 | *((long *)data->data)); | ||
| 560 | default: | ||
| 561 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 562 | }; | ||
| 563 | |||
| 564 | return ML_SUCCESS; | ||
| 565 | } | ||
| 566 | |||
| 567 | static int kxtf9_get_config(void *mlsl_handle, | ||
| 568 | struct ext_slave_descr *slave, | ||
| 569 | struct ext_slave_platform_data *pdata, | ||
| 570 | struct ext_slave_config *data) | ||
| 571 | { | ||
| 572 | struct kxtf9_private_data *private_data = pdata->private_data; | ||
| 573 | if (!data->data) | ||
| 574 | return ML_ERROR_INVALID_PARAMETER; | ||
| 575 | |||
| 576 | switch (data->key) { | ||
| 577 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 578 | (*(unsigned long *)data->data) = | ||
| 579 | (unsigned long) private_data->suspend.odr; | ||
| 580 | break; | ||
| 581 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 582 | (*(unsigned long *)data->data) = | ||
| 583 | (unsigned long) private_data->resume.odr; | ||
| 584 | break; | ||
| 585 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 586 | (*(unsigned long *)data->data) = | ||
| 587 | (unsigned long) private_data->suspend.fsr; | ||
| 588 | break; | ||
| 589 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 590 | (*(unsigned long *)data->data) = | ||
| 591 | (unsigned long) private_data->resume.fsr; | ||
| 592 | break; | ||
| 593 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 594 | (*(unsigned long *)data->data) = | ||
| 595 | (unsigned long) private_data->suspend.ths; | ||
| 596 | break; | ||
| 597 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 598 | (*(unsigned long *)data->data) = | ||
| 599 | (unsigned long) private_data->resume.ths; | ||
| 600 | break; | ||
| 601 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 602 | (*(unsigned long *)data->data) = | ||
| 603 | (unsigned long) private_data->suspend.dur; | ||
| 604 | break; | ||
| 605 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 606 | (*(unsigned long *)data->data) = | ||
| 607 | (unsigned long) private_data->resume.dur; | ||
| 608 | break; | ||
| 609 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 610 | (*(unsigned long *)data->data) = | ||
| 611 | (unsigned long) private_data->suspend.irq_type; | ||
| 612 | break; | ||
| 613 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 614 | (*(unsigned long *)data->data) = | ||
| 615 | (unsigned long) private_data->resume.irq_type; | ||
| 616 | break; | ||
| 617 | default: | ||
| 618 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 619 | }; | ||
| 620 | |||
| 621 | return ML_SUCCESS; | ||
| 622 | } | ||
| 623 | |||
| 624 | static int kxtf9_read(void *mlsl_handle, | ||
| 625 | struct ext_slave_descr *slave, | ||
| 626 | struct ext_slave_platform_data *pdata, | ||
| 627 | unsigned char *data) | ||
| 628 | { | ||
| 629 | int result; | ||
| 630 | unsigned char reg; | ||
| 631 | result = MLSLSerialRead(mlsl_handle, pdata->address, | ||
| 632 | KXTF9_INT_SRC_REG2, 1, ®); | ||
| 633 | ERROR_CHECK(result); | ||
| 634 | |||
| 635 | if (!(reg & 0x10)) | ||
| 636 | return ML_ERROR_ACCEL_DATA_NOT_READY; | ||
| 637 | |||
| 638 | result = MLSLSerialRead(mlsl_handle, pdata->address, | ||
| 639 | slave->reg, slave->len, data); | ||
| 640 | ERROR_CHECK(result); | ||
| 641 | return result; | ||
| 642 | } | ||
| 643 | |||
| 644 | static struct ext_slave_descr kxtf9_descr = { | ||
| 645 | /*.init = */ kxtf9_init, | ||
| 646 | /*.exit = */ kxtf9_exit, | ||
| 647 | /*.suspend = */ kxtf9_suspend, | ||
| 648 | /*.resume = */ kxtf9_resume, | ||
| 649 | /*.read = */ kxtf9_read, | ||
| 650 | /*.config = */ kxtf9_config, | ||
| 651 | /*.get_config = */ kxtf9_get_config, | ||
| 652 | /*.name = */ "kxtf9", | ||
| 653 | /*.type = */ EXT_SLAVE_TYPE_ACCELEROMETER, | ||
| 654 | /*.id = */ ACCEL_ID_KXTF9, | ||
| 655 | /*.reg = */ 0x06, | ||
| 656 | /*.len = */ 6, | ||
| 657 | /*.endian = */ EXT_SLAVE_LITTLE_ENDIAN, | ||
| 658 | /*.range = */ {2, 0}, | ||
| 659 | }; | ||
| 660 | |||
| 661 | struct ext_slave_descr *kxtf9_get_slave_descr(void) | ||
| 662 | { | ||
| 663 | return &kxtf9_descr; | ||
| 664 | } | ||
| 665 | EXPORT_SYMBOL(kxtf9_get_slave_descr); | ||
| 666 | |||
| 667 | /** | ||
| 668 | * @} | ||
| 669 | **/ | ||
diff --git a/drivers/misc/mpu3050/compass/ak8975.c b/drivers/misc/mpu3050/compass/ak8975.c new file mode 100644 index 00000000000..b8aed30ba39 --- /dev/null +++ b/drivers/misc/mpu3050/compass/ak8975.c | |||
| @@ -0,0 +1,258 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2010 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @defgroup COMPASSDL (Motion Library - Accelerometer Driver Layer) | ||
| 22 | * @brief Provides the interface to setup and handle an accelerometers | ||
| 23 | * connected to the secondary I2C interface of the gyroscope. | ||
| 24 | * | ||
| 25 | * @{ | ||
| 26 | * @file AK8975.c | ||
| 27 | * @brief Magnetometer setup and handling methods for AKM 8975 compass. | ||
| 28 | */ | ||
| 29 | |||
| 30 | /* ------------------ */ | ||
| 31 | /* - Include Files. - */ | ||
| 32 | /* ------------------ */ | ||
| 33 | |||
| 34 | #include <string.h> | ||
| 35 | |||
| 36 | #ifdef __KERNEL__ | ||
| 37 | #include <linux/module.h> | ||
| 38 | #endif | ||
| 39 | |||
| 40 | #include "mpu.h" | ||
| 41 | #include "mlsl.h" | ||
| 42 | #include "mlos.h" | ||
| 43 | |||
| 44 | #include <log.h> | ||
| 45 | #undef MPL_LOG_TAG | ||
| 46 | #define MPL_LOG_TAG "MPL-compass" | ||
| 47 | |||
| 48 | |||
| 49 | #define AK8975_REG_ST1 (0x02) | ||
| 50 | #define AK8975_REG_HXL (0x03) | ||
| 51 | #define AK8975_REG_ST2 (0x09) | ||
| 52 | |||
| 53 | #define AK8975_REG_CNTL (0x0A) | ||
| 54 | |||
| 55 | #define AK8975_CNTL_MODE_POWER_DOWN (0x00) | ||
| 56 | #define AK8975_CNTL_MODE_SINGLE_MEASUREMENT (0x01) | ||
| 57 | |||
| 58 | int ak8975_suspend(void *mlsl_handle, | ||
| 59 | struct ext_slave_descr *slave, | ||
| 60 | struct ext_slave_platform_data *pdata) | ||
| 61 | { | ||
| 62 | int result = ML_SUCCESS; | ||
| 63 | result = | ||
| 64 | MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 65 | AK8975_REG_CNTL, | ||
| 66 | AK8975_CNTL_MODE_POWER_DOWN); | ||
| 67 | MLOSSleep(1); /* wait at least 100us */ | ||
| 68 | ERROR_CHECK(result); | ||
| 69 | return result; | ||
| 70 | } | ||
| 71 | |||
| 72 | int ak8975_resume(void *mlsl_handle, | ||
| 73 | struct ext_slave_descr *slave, | ||
| 74 | struct ext_slave_platform_data *pdata) | ||
| 75 | { | ||
| 76 | int result = ML_SUCCESS; | ||
| 77 | result = | ||
| 78 | MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 79 | AK8975_REG_CNTL, | ||
| 80 | AK8975_CNTL_MODE_SINGLE_MEASUREMENT); | ||
| 81 | ERROR_CHECK(result); | ||
| 82 | return result; | ||
| 83 | } | ||
| 84 | |||
| 85 | int ak8975_read(void *mlsl_handle, | ||
| 86 | struct ext_slave_descr *slave, | ||
| 87 | struct ext_slave_platform_data *pdata, unsigned char *data) | ||
| 88 | { | ||
| 89 | unsigned char regs[8]; | ||
| 90 | unsigned char *stat = ®s[0]; | ||
| 91 | unsigned char *stat2 = ®s[7]; | ||
| 92 | int result = ML_SUCCESS; | ||
| 93 | int status = ML_SUCCESS; | ||
| 94 | |||
| 95 | result = | ||
| 96 | MLSLSerialRead(mlsl_handle, pdata->address, AK8975_REG_ST1, | ||
| 97 | 8, regs); | ||
| 98 | ERROR_CHECK(result); | ||
| 99 | |||
| 100 | /* | ||
| 101 | * ST : data ready - | ||
| 102 | * Measurement has been completed and data is ready to be read. | ||
| 103 | */ | ||
| 104 | if (*stat & 0x01) { | ||
| 105 | memcpy(data, ®s[1], 6); | ||
| 106 | status = ML_SUCCESS; | ||
| 107 | } | ||
| 108 | |||
| 109 | /* | ||
| 110 | * ST2 : data error - | ||
| 111 | * occurs when data read is started outside of a readable period; | ||
| 112 | * data read would not be correct. | ||
| 113 | * Valid in continuous measurement mode only. | ||
| 114 | * In single measurement mode this error should not occour but we | ||
| 115 | * stil account for it and return an error, since the data would be | ||
| 116 | * corrupted. | ||
| 117 | * DERR bit is self-clearing when ST2 register is read. | ||
| 118 | */ | ||
| 119 | if (*stat2 & 0x04) | ||
| 120 | status = ML_ERROR_COMPASS_DATA_ERROR; | ||
| 121 | /* | ||
| 122 | * ST2 : overflow - | ||
| 123 | * the sum of the absolute values of all axis |X|+|Y|+|Z| < 2400uT. | ||
| 124 | * This is likely to happen in presence of an external magnetic | ||
| 125 | * disturbance; it indicates, the sensor data is incorrect and should | ||
| 126 | * be ignored. | ||
| 127 | * An error is returned. | ||
| 128 | * HOFL bit clears when a new measurement starts. | ||
| 129 | */ | ||
| 130 | if (*stat2 & 0x08) | ||
| 131 | status = ML_ERROR_COMPASS_DATA_OVERFLOW; | ||
| 132 | /* | ||
| 133 | * ST : overrun - | ||
| 134 | * the previous sample was not fetched and lost. | ||
| 135 | * Valid in continuous measurement mode only. | ||
| 136 | * In single measurement mode this error should not occour and we | ||
| 137 | * don't consider this condition an error. | ||
| 138 | * DOR bit is self-clearing when ST2 or any meas. data register is | ||
| 139 | * read. | ||
| 140 | */ | ||
| 141 | if (*stat & 0x02) { | ||
| 142 | /* status = ML_ERROR_COMPASS_DATA_UNDERFLOW; */ | ||
| 143 | status = ML_SUCCESS; | ||
| 144 | } | ||
| 145 | |||
| 146 | /* | ||
| 147 | * trigger next measurement if: | ||
| 148 | * - stat is non zero; | ||
| 149 | * - if stat is zero and stat2 is non zero. | ||
| 150 | * Won't trigger if data is not ready and there was no error. | ||
| 151 | */ | ||
| 152 | if (*stat != 0x00 || *stat2 != 0x00) { | ||
| 153 | result = | ||
| 154 | MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
| 155 | AK8975_REG_CNTL, | ||
| 156 | AK8975_CNTL_MODE_SINGLE_MEASUREMENT); | ||
| 157 | ERROR_CHECK(result); | ||
| 158 | } | ||
| 159 | |||
| 160 | return status; | ||
| 161 | } | ||
| 162 | |||
| 163 | static int ak8975_config(void *mlsl_handle, | ||
| 164 | struct ext_slave_descr *slave, | ||
| 165 | struct ext_slave_platform_data *pdata, | ||
| 166 | struct ext_slave_config *data) | ||
| 167 | { | ||
| 168 | int result; | ||
| 169 | if (!data->data) | ||
| 170 | return ML_ERROR_INVALID_PARAMETER; | ||
| 171 | |||
| 172 | switch (data->key) { | ||
| 173 | case MPU_SLAVE_WRITE_REGISTERS: | ||
| 174 | result = MLSLSerialWrite(mlsl_handle, pdata->address, | ||
| 175 | data->len, | ||
| 176 | (unsigned char *)data->data); | ||
| 177 | ERROR_CHECK(result); | ||
| 178 | break; | ||
| 179 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 180 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 181 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 182 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 183 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 184 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 185 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 186 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 187 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 188 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 189 | default: | ||
| 190 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 191 | }; | ||
| 192 | |||
| 193 | return ML_SUCCESS; | ||
| 194 | } | ||
| 195 | |||
| 196 | static int ak8975_get_config(void *mlsl_handle, | ||
| 197 | struct ext_slave_descr *slave, | ||
| 198 | struct ext_slave_platform_data *pdata, | ||
| 199 | struct ext_slave_config *data) | ||
| 200 | { | ||
| 201 | int result; | ||
| 202 | if (!data->data) | ||
| 203 | return ML_ERROR_INVALID_PARAMETER; | ||
| 204 | |||
| 205 | switch (data->key) { | ||
| 206 | case MPU_SLAVE_READ_REGISTERS: | ||
| 207 | { | ||
| 208 | unsigned char *serial_data = (unsigned char *)data->data; | ||
| 209 | result = MLSLSerialRead(mlsl_handle, pdata->address, | ||
| 210 | serial_data[0], | ||
| 211 | data->len - 1, | ||
| 212 | &serial_data[1]); | ||
| 213 | ERROR_CHECK(result); | ||
| 214 | break; | ||
| 215 | } | ||
| 216 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 217 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 218 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 219 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 220 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 221 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 222 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 223 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 224 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 225 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 226 | default: | ||
| 227 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 228 | }; | ||
| 229 | |||
| 230 | return ML_SUCCESS; | ||
| 231 | } | ||
| 232 | |||
| 233 | struct ext_slave_descr ak8975_descr = { | ||
| 234 | /*.init = */ NULL, | ||
| 235 | /*.exit = */ NULL, | ||
| 236 | /*.suspend = */ ak8975_suspend, | ||
| 237 | /*.resume = */ ak8975_resume, | ||
| 238 | /*.read = */ ak8975_read, | ||
| 239 | /*.config = */ ak8975_config, | ||
| 240 | /*.get_config = */ ak8975_get_config, | ||
| 241 | /*.name = */ "ak8975", | ||
| 242 | /*.type = */ EXT_SLAVE_TYPE_COMPASS, | ||
| 243 | /*.id = */ COMPASS_ID_AKM, | ||
| 244 | /*.reg = */ 0x01, | ||
| 245 | /*.len = */ 9, | ||
| 246 | /*.endian = */ EXT_SLAVE_LITTLE_ENDIAN, | ||
| 247 | /*.range = */ {9830, 4000} | ||
| 248 | }; | ||
| 249 | |||
| 250 | struct ext_slave_descr *ak8975_get_slave_descr(void) | ||
| 251 | { | ||
| 252 | return &ak8975_descr; | ||
| 253 | } | ||
| 254 | EXPORT_SYMBOL(ak8975_get_slave_descr); | ||
| 255 | |||
| 256 | /** | ||
| 257 | * @} | ||
| 258 | */ | ||
diff --git a/drivers/misc/mpu3050/log.h b/drivers/misc/mpu3050/log.h new file mode 100644 index 00000000000..f2f9ea7ece8 --- /dev/null +++ b/drivers/misc/mpu3050/log.h | |||
| @@ -0,0 +1,306 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2010 InvenSense Inc | ||
| 3 | * | ||
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | * you may not use this file except in compliance with the License. | ||
| 6 | * You may obtain a copy of the License at | ||
| 7 | * | ||
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | * | ||
| 10 | * Unless required by applicable law or agreed to in writing, software | ||
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | * See the License for the specific language governing permissions and | ||
| 14 | * limitations under the License. | ||
| 15 | */ | ||
| 16 | |||
| 17 | /* | ||
| 18 | * C/C++ logging functions. See the logging documentation for API details. | ||
| 19 | * | ||
| 20 | * We'd like these to be available from C code (in case we import some from | ||
| 21 | * somewhere), so this has a C interface. | ||
| 22 | * | ||
| 23 | * The output will be correct when the log file is shared between multiple | ||
| 24 | * threads and/or multiple processes so long as the operating system | ||
| 25 | * supports O_APPEND. These calls have mutex-protected data structures | ||
| 26 | * and so are NOT reentrant. Do not use MPL_LOG in a signal handler. | ||
| 27 | */ | ||
| 28 | #ifndef _LIBS_CUTILS_MPL_LOG_H | ||
| 29 | #define _LIBS_CUTILS_MPL_LOG_H | ||
| 30 | |||
| 31 | #include <stdarg.h> | ||
| 32 | |||
| 33 | #ifdef ANDROID | ||
| 34 | #include <utils/Log.h> /* For the LOG macro */ | ||
| 35 | #endif | ||
| 36 | |||
| 37 | #ifdef __KERNEL__ | ||
| 38 | #include <linux/kernel.h> | ||
| 39 | #endif | ||
| 40 | |||
| 41 | #ifdef __cplusplus | ||
| 42 | extern "C" { | ||
| 43 | #endif | ||
| 44 | |||
| 45 | /* --------------------------------------------------------------------- */ | ||
| 46 | |||
| 47 | /* | ||
| 48 | * Normally we strip MPL_LOGV (VERBOSE messages) from release builds. | ||
| 49 | * You can modify this (for example with "#define MPL_LOG_NDEBUG 0" | ||
| 50 | * at the top of your source file) to change that behavior. | ||
| 51 | */ | ||
| 52 | #ifndef MPL_LOG_NDEBUG | ||
| 53 | #ifdef NDEBUG | ||
| 54 | #define MPL_LOG_NDEBUG 1 | ||
| 55 | #else | ||
| 56 | #define MPL_LOG_NDEBUG 0 | ||
| 57 | #endif | ||
| 58 | #endif | ||
| 59 | |||
| 60 | #ifdef __KERNEL__ | ||
| 61 | #define MPL_LOG_UNKNOWN MPL_LOG_VERBOSE | ||
| 62 | #define MPL_LOG_DEFAULT KERN_DEFAULT | ||
| 63 | #define MPL_LOG_VERBOSE KERN_CONT | ||
| 64 | #define MPL_LOG_DEBUG KERN_NOTICE | ||
| 65 | #define MPL_LOG_INFO KERN_INFO | ||
| 66 | #define MPL_LOG_WARN KERN_WARNING | ||
| 67 | #define MPL_LOG_ERROR KERN_ERR | ||
| 68 | #define MPL_LOG_SILENT MPL_LOG_VERBOSE | ||
| 69 | |||
| 70 | #else | ||
| 71 | /* Based off the log priorities in android | ||
| 72 | /system/core/include/android/log.h */ | ||
| 73 | #define MPL_LOG_UNKNOWN (0) | ||
| 74 | #define MPL_LOG_DEFAULT (1) | ||
| 75 | #define MPL_LOG_VERBOSE (2) | ||
| 76 | #define MPL_LOG_DEBUG (3) | ||
| 77 | #define MPL_LOG_INFO (4) | ||
| 78 | #define MPL_LOG_WARN (5) | ||
| 79 | #define MPL_LOG_ERROR (6) | ||
| 80 | #define MPL_LOG_SILENT (8) | ||
| 81 | #endif | ||
| 82 | |||
| 83 | |||
| 84 | /* | ||
| 85 | * This is the local tag used for the following simplified | ||
| 86 | * logging macros. You can change this preprocessor definition | ||
| 87 | * before using the other macros to change the tag. | ||
| 88 | */ | ||
| 89 | #ifndef MPL_LOG_TAG | ||
| 90 | #ifdef __KERNEL__ | ||
| 91 | #define MPL_LOG_TAG | ||
| 92 | #else | ||
| 93 | #define MPL_LOG_TAG NULL | ||
| 94 | #endif | ||
| 95 | #endif | ||
| 96 | |||
| 97 | /* --------------------------------------------------------------------- */ | ||
| 98 | |||
| 99 | /* | ||
| 100 | * Simplified macro to send a verbose log message using the current MPL_LOG_TAG. | ||
| 101 | */ | ||
| 102 | #ifndef MPL_LOGV | ||
| 103 | #if MPL_LOG_NDEBUG | ||
| 104 | #define MPL_LOGV(fmt, ...) \ | ||
| 105 | do { \ | ||
| 106 | if (0) \ | ||
| 107 | MPL_LOG(LOG_VERBOSE, MPL_LOG_TAG, fmt, ##__VA_ARGS__);\ | ||
| 108 | } while (0) | ||
| 109 | #else | ||
| 110 | #define MPL_LOGV(fmt, ...) MPL_LOG(LOG_VERBOSE, MPL_LOG_TAG, fmt, ##__VA_ARGS__) | ||
| 111 | #endif | ||
| 112 | #endif | ||
| 113 | |||
| 114 | #ifndef CONDITION | ||
| 115 | #define CONDITION(cond) ((cond) != 0) | ||
| 116 | #endif | ||
| 117 | |||
| 118 | #ifndef MPL_LOGV_IF | ||
| 119 | #if MPL_LOG_NDEBUG | ||
| 120 | #define MPL_LOGV_IF(cond, fmt, ...) \ | ||
| 121 | do { if (0) MPL_LOG(fmt, ##__VA_ARGS__); } while (0) | ||
| 122 | #else | ||
| 123 | #define MPL_LOGV_IF(cond, fmt, ...) \ | ||
| 124 | ((CONDITION(cond)) \ | ||
| 125 | ? MPL_LOG(LOG_VERBOSE, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \ | ||
| 126 | : (void)0) | ||
| 127 | #endif | ||
| 128 | #endif | ||
| 129 | |||
| 130 | /* | ||
| 131 | * Simplified macro to send a debug log message using the current MPL_LOG_TAG. | ||
| 132 | */ | ||
| 133 | #ifndef MPL_LOGD | ||
| 134 | #define MPL_LOGD(fmt, ...) MPL_LOG(LOG_DEBUG, MPL_LOG_TAG, fmt, ##__VA_ARGS__) | ||
| 135 | #endif | ||
| 136 | |||
| 137 | #ifndef MPL_LOGD_IF | ||
| 138 | #define MPL_LOGD_IF(cond, fmt, ...) \ | ||
| 139 | ((CONDITION(cond)) \ | ||
| 140 | ? MPL_LOG(LOG_DEBUG, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \ | ||
| 141 | : (void)0) | ||
| 142 | #endif | ||
| 143 | |||
| 144 | /* | ||
| 145 | * Simplified macro to send an info log message using the current MPL_LOG_TAG. | ||
| 146 | */ | ||
| 147 | #ifndef MPL_LOGI | ||
| 148 | #define MPL_LOGI(fmt, ...) MPL_LOG(LOG_INFO, MPL_LOG_TAG, fmt, ##__VA_ARGS__) | ||
| 149 | #endif | ||
| 150 | |||
| 151 | #ifndef MPL_LOGI_IF | ||
| 152 | #define MPL_LOGI_IF(cond, fmt, ...) \ | ||
| 153 | ((CONDITION(cond)) \ | ||
| 154 | ? MPL_LOG(LOG_INFO, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \ | ||
| 155 | : (void)0) | ||
| 156 | #endif | ||
| 157 | |||
| 158 | /* | ||
| 159 | * Simplified macro to send a warning log message using the current MPL_LOG_TAG. | ||
| 160 | */ | ||
| 161 | #ifndef MPL_LOGW | ||
| 162 | #ifdef __KERNEL__ | ||
| 163 | #define MPL_LOGW(fmt, ...) printk(KERN_WARNING MPL_LOG_TAG fmt, ##__VA_ARGS__) | ||
| 164 | #else | ||
| 165 | #define MPL_LOGW(fmt, ...) MPL_LOG(LOG_WARN, MPL_LOG_TAG, fmt, ##__VA_ARGS__) | ||
| 166 | #endif | ||
| 167 | #endif | ||
| 168 | |||
| 169 | #ifndef MPL_LOGW_IF | ||
| 170 | #define MPL_LOGW_IF(cond, fmt, ...) \ | ||
| 171 | ((CONDITION(cond)) \ | ||
| 172 | ? MPL_LOG(LOG_WARN, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \ | ||
| 173 | : (void)0) | ||
| 174 | #endif | ||
| 175 | |||
| 176 | /* | ||
| 177 | * Simplified macro to send an error log message using the current MPL_LOG_TAG. | ||
| 178 | */ | ||
| 179 | #ifndef MPL_LOGE | ||
| 180 | #ifdef __KERNEL__ | ||
| 181 | #define MPL_LOGE(fmt, ...) printk(KERN_ERR MPL_LOG_TAG fmt, ##__VA_ARGS__) | ||
| 182 | #else | ||
| 183 | #define MPL_LOGE(fmt, ...) MPL_LOG(LOG_ERROR, MPL_LOG_TAG, fmt, ##__VA_ARGS__) | ||
| 184 | #endif | ||
| 185 | #endif | ||
| 186 | |||
| 187 | #ifndef MPL_LOGE_IF | ||
| 188 | #define MPL_LOGE_IF(cond, fmt, ...) \ | ||
| 189 | ((CONDITION(cond)) \ | ||
| 190 | ? MPL_LOG(LOG_ERROR, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \ | ||
| 191 | : (void)0) | ||
| 192 | #endif | ||
| 193 | |||
| 194 | /* --------------------------------------------------------------------- */ | ||
| 195 | |||
| 196 | /* | ||
| 197 | * Log a fatal error. If the given condition fails, this stops program | ||
| 198 | * execution like a normal assertion, but also generating the given message. | ||
| 199 | * It is NOT stripped from release builds. Note that the condition test | ||
| 200 | * is -inverted- from the normal assert() semantics. | ||
| 201 | */ | ||
| 202 | #define MPL_LOG_ALWAYS_FATAL_IF(cond, fmt, ...) \ | ||
| 203 | ((CONDITION(cond)) \ | ||
| 204 | ? ((void)android_printAssert(#cond, MPL_LOG_TAG, \ | ||
| 205 | fmt, ##__VA_ARGS__)) \ | ||
| 206 | : (void)0) | ||
| 207 | |||
| 208 | #define MPL_LOG_ALWAYS_FATAL(fmt, ...) \ | ||
| 209 | (((void)android_printAssert(NULL, MPL_LOG_TAG, fmt, ##__VA_ARGS__))) | ||
| 210 | |||
| 211 | /* | ||
| 212 | * Versions of MPL_LOG_ALWAYS_FATAL_IF and MPL_LOG_ALWAYS_FATAL that | ||
| 213 | * are stripped out of release builds. | ||
| 214 | */ | ||
| 215 | #if MPL_LOG_NDEBUG | ||
| 216 | #define MPL_LOG_FATAL_IF(cond, fmt, ...) \ | ||
| 217 | do { \ | ||
| 218 | if (0) \ | ||
| 219 | MPL_LOG_ALWAYS_FATAL_IF(cond, fmt, ##__VA_ARGS__); \ | ||
| 220 | } while (0) | ||
| 221 | #define MPL_LOG_FATAL(fmt, ...) \ | ||
| 222 | do { \ | ||
| 223 | if (0) \ | ||
| 224 | MPL_LOG_ALWAYS_FATAL(fmt, ##__VA_ARGS__) \ | ||
| 225 | } while (0) | ||
| 226 | #else | ||
| 227 | #define MPL_LOG_FATAL_IF(cond, fmt, ...) \ | ||
| 228 | MPL_LOG_ALWAYS_FATAL_IF(cond, fmt, ##__VA_ARGS__) | ||
| 229 | #define MPL_LOG_FATAL(fmt, ...) \ | ||
| 230 | MPL_LOG_ALWAYS_FATAL(fmt, ##__VA_ARGS__) | ||
| 231 | #endif | ||
| 232 | |||
| 233 | /* | ||
| 234 | * Assertion that generates a log message when the assertion fails. | ||
| 235 | * Stripped out of release builds. Uses the current MPL_LOG_TAG. | ||
| 236 | */ | ||
| 237 | #define MPL_LOG_ASSERT(cond, fmt, ...) \ | ||
| 238 | MPL_LOG_FATAL_IF(!(cond), fmt, ##__VA_ARGS__) | ||
| 239 | |||
| 240 | /* --------------------------------------------------------------------- */ | ||
| 241 | |||
| 242 | /* | ||
| 243 | * Basic log message macro. | ||
| 244 | * | ||
| 245 | * Example: | ||
| 246 | * MPL_LOG(MPL_LOG_WARN, NULL, "Failed with error %d", errno); | ||
| 247 | * | ||
| 248 | * The second argument may be NULL or "" to indicate the "global" tag. | ||
| 249 | */ | ||
| 250 | #ifndef MPL_LOG | ||
| 251 | #define MPL_LOG(priority, tag, fmt, ...) \ | ||
| 252 | MPL_LOG_PRI(priority, tag, fmt, ##__VA_ARGS__) | ||
| 253 | #endif | ||
| 254 | |||
| 255 | /* | ||
| 256 | * Log macro that allows you to specify a number for the priority. | ||
| 257 | */ | ||
| 258 | #ifndef MPL_LOG_PRI | ||
| 259 | #ifdef ANDROID | ||
| 260 | #define MPL_LOG_PRI(priority, tag, fmt, ...) \ | ||
| 261 | LOG(priority, tag, fmt, ##__VA_ARGS__) | ||
| 262 | #elif defined __KERNEL__ | ||
| 263 | #define MPL_LOG_PRI(priority, tag, fmt, ...) \ | ||
| 264 | pr_debug(MPL_##priority tag fmt, ##__VA_ARGS__) | ||
| 265 | #else | ||
| 266 | #define MPL_LOG_PRI(priority, tag, fmt, ...) \ | ||
| 267 | _MLPrintLog(MPL_##priority, tag, fmt, ##__VA_ARGS__) | ||
| 268 | #endif | ||
| 269 | #endif | ||
| 270 | |||
| 271 | /* | ||
| 272 | * Log macro that allows you to pass in a varargs ("args" is a va_list). | ||
| 273 | */ | ||
| 274 | #ifndef MPL_LOG_PRI_VA | ||
| 275 | #ifdef ANDROID | ||
| 276 | #define MPL_LOG_PRI_VA(priority, tag, fmt, args) \ | ||
| 277 | android_vprintLog(priority, NULL, tag, fmt, args) | ||
| 278 | #elif defined __KERNEL__ | ||
| 279 | /* not allowed in the Kernel because there is no dev_dbg that takes a va_list */ | ||
| 280 | #else | ||
| 281 | #define MPL_LOG_PRI_VA(priority, tag, fmt, args) \ | ||
| 282 | _MLPrintVaLog(priority, NULL, tag, fmt, args) | ||
| 283 | #endif | ||
| 284 | #endif | ||
| 285 | |||
| 286 | /* --------------------------------------------------------------------- */ | ||
| 287 | |||
| 288 | /* | ||
| 289 | * =========================================================================== | ||
| 290 | * | ||
| 291 | * The stuff in the rest of this file should not be used directly. | ||
| 292 | */ | ||
| 293 | |||
| 294 | #ifndef ANDROID | ||
| 295 | int _MLPrintLog(int priority, const char *tag, const char *fmt, | ||
| 296 | ...); | ||
| 297 | int _MLPrintVaLog(int priority, const char *tag, const char *fmt, | ||
| 298 | va_list args); | ||
| 299 | /* Final implementation of actual writing to a character device */ | ||
| 300 | int _MLWriteLog(const char *buf, int buflen); | ||
| 301 | #endif | ||
| 302 | |||
| 303 | #ifdef __cplusplus | ||
| 304 | } | ||
| 305 | #endif | ||
| 306 | #endif /* _LIBS_CUTILS_MPL_LOG_H */ | ||
diff --git a/drivers/misc/mpu3050/mldl_cfg.c b/drivers/misc/mpu3050/mldl_cfg.c new file mode 100644 index 00000000000..9cc4cf69038 --- /dev/null +++ b/drivers/misc/mpu3050/mldl_cfg.c | |||
| @@ -0,0 +1,1739 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2010 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup MLDL | ||
| 22 | * | ||
| 23 | * @{ | ||
| 24 | * @file mldl_cfg.c | ||
| 25 | * @brief The Motion Library Driver Layer. | ||
| 26 | */ | ||
| 27 | |||
| 28 | /* ------------------ */ | ||
| 29 | /* - Include Files. - */ | ||
| 30 | /* ------------------ */ | ||
| 31 | |||
| 32 | #include <stddef.h> | ||
| 33 | |||
| 34 | #include "mldl_cfg.h" | ||
| 35 | #include "mpu.h" | ||
| 36 | |||
| 37 | #include "mlsl.h" | ||
| 38 | #include "mlos.h" | ||
| 39 | |||
| 40 | #include "log.h" | ||
| 41 | #undef MPL_LOG_TAG | ||
| 42 | #define MPL_LOG_TAG "mldl_cfg:" | ||
| 43 | |||
| 44 | /* --------------------- */ | ||
| 45 | /* - Variables. - */ | ||
| 46 | /* --------------------- */ | ||
| 47 | #ifdef M_HW | ||
| 48 | #define SLEEP 0 | ||
| 49 | #define WAKE_UP 7 | ||
| 50 | #define RESET 1 | ||
| 51 | #define STANDBY 1 | ||
| 52 | #else | ||
| 53 | /* licteral significance of all parameters used in MLDLPowerMgmtMPU */ | ||
| 54 | #define SLEEP 1 | ||
| 55 | #define WAKE_UP 0 | ||
| 56 | #define RESET 1 | ||
| 57 | #define STANDBY 1 | ||
| 58 | #endif | ||
| 59 | |||
| 60 | /*---------------------*/ | ||
| 61 | /*- Prototypes. -*/ | ||
| 62 | /*---------------------*/ | ||
| 63 | |||
| 64 | /*----------------------*/ | ||
| 65 | /*- Static Functions. -*/ | ||
| 66 | /*----------------------*/ | ||
| 67 | |||
| 68 | static int dmp_stop(struct mldl_cfg *mldl_cfg, void *gyro_handle) | ||
| 69 | { | ||
| 70 | unsigned char userCtrlReg; | ||
| 71 | int result; | ||
| 72 | |||
| 73 | if (!mldl_cfg->dmp_is_running) | ||
| 74 | return ML_SUCCESS; | ||
| 75 | |||
| 76 | result = MLSLSerialRead(gyro_handle, mldl_cfg->addr, | ||
| 77 | MPUREG_USER_CTRL, 1, &userCtrlReg); | ||
| 78 | ERROR_CHECK(result); | ||
| 79 | userCtrlReg = (userCtrlReg & (~BIT_FIFO_EN)) | BIT_FIFO_RST; | ||
| 80 | userCtrlReg = (userCtrlReg & (~BIT_DMP_EN)) | BIT_DMP_RST; | ||
| 81 | |||
| 82 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
| 83 | MPUREG_USER_CTRL, userCtrlReg); | ||
| 84 | ERROR_CHECK(result); | ||
| 85 | mldl_cfg->dmp_is_running = 0; | ||
| 86 | |||
| 87 | return result; | ||
| 88 | |||
| 89 | } | ||
| 90 | /** | ||
| 91 | * @brief Starts the DMP running | ||
| 92 | * | ||
| 93 | * @return ML_SUCCESS or non-zero error code | ||
| 94 | */ | ||
| 95 | static int dmp_start(struct mldl_cfg *pdata, void *mlsl_handle) | ||
| 96 | { | ||
| 97 | unsigned char userCtrlReg; | ||
| 98 | int result; | ||
| 99 | |||
| 100 | if (pdata->dmp_is_running == pdata->dmp_enable) | ||
| 101 | return ML_SUCCESS; | ||
| 102 | |||
| 103 | result = MLSLSerialRead(mlsl_handle, pdata->addr, | ||
| 104 | MPUREG_USER_CTRL, 1, &userCtrlReg); | ||
| 105 | ERROR_CHECK(result); | ||
| 106 | |||
| 107 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
| 108 | MPUREG_USER_CTRL, | ||
| 109 | ((userCtrlReg & (~BIT_FIFO_EN)) | ||
| 110 | | BIT_FIFO_RST)); | ||
| 111 | ERROR_CHECK(result); | ||
| 112 | |||
| 113 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
| 114 | MPUREG_USER_CTRL, userCtrlReg); | ||
| 115 | ERROR_CHECK(result); | ||
| 116 | |||
| 117 | result = MLSLSerialRead(mlsl_handle, pdata->addr, | ||
| 118 | MPUREG_USER_CTRL, 1, &userCtrlReg); | ||
| 119 | ERROR_CHECK(result); | ||
| 120 | |||
| 121 | if (pdata->dmp_enable) | ||
| 122 | userCtrlReg |= BIT_DMP_EN; | ||
| 123 | else | ||
| 124 | userCtrlReg &= ~BIT_DMP_EN; | ||
| 125 | |||
| 126 | if (pdata->fifo_enable) | ||
| 127 | userCtrlReg |= BIT_FIFO_EN; | ||
| 128 | else | ||
| 129 | userCtrlReg &= ~BIT_FIFO_EN; | ||
| 130 | |||
| 131 | userCtrlReg |= BIT_DMP_RST; | ||
| 132 | |||
| 133 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
| 134 | MPUREG_USER_CTRL, userCtrlReg); | ||
| 135 | ERROR_CHECK(result); | ||
| 136 | pdata->dmp_is_running = pdata->dmp_enable; | ||
| 137 | |||
| 138 | return result; | ||
| 139 | } | ||
| 140 | |||
| 141 | /** | ||
| 142 | * @brief enables/disables the I2C bypass to an external device | ||
| 143 | * connected to MPU's secondary I2C bus. | ||
| 144 | * @param enable | ||
| 145 | * Non-zero to enable pass through. | ||
| 146 | * @return ML_SUCCESS if successful, a non-zero error code otherwise. | ||
| 147 | */ | ||
| 148 | static int MLDLSetI2CBypass(struct mldl_cfg *mldl_cfg, | ||
| 149 | void *mlsl_handle, | ||
| 150 | unsigned char enable) | ||
| 151 | { | ||
| 152 | unsigned char b; | ||
| 153 | int result; | ||
| 154 | |||
| 155 | if ((mldl_cfg->gyro_is_bypassed && enable) || | ||
| 156 | (!mldl_cfg->gyro_is_bypassed && !enable)) | ||
| 157 | return ML_SUCCESS; | ||
| 158 | |||
| 159 | /*---- get current 'USER_CTRL' into b ----*/ | ||
| 160 | result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr, | ||
| 161 | MPUREG_USER_CTRL, 1, &b); | ||
| 162 | ERROR_CHECK(result); | ||
| 163 | |||
| 164 | b &= ~BIT_AUX_IF_EN; | ||
| 165 | |||
| 166 | if (!enable) { | ||
| 167 | result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr, | ||
| 168 | MPUREG_USER_CTRL, | ||
| 169 | (b | BIT_AUX_IF_EN)); | ||
| 170 | ERROR_CHECK(result); | ||
| 171 | } else { | ||
| 172 | /* Coming out of I2C is tricky due to several erratta. Do not | ||
| 173 | * modify this algorithm | ||
| 174 | */ | ||
| 175 | /* | ||
| 176 | * 1) wait for the right time and send the command to change | ||
| 177 | * the aux i2c slave address to an invalid address that will | ||
| 178 | * get nack'ed | ||
| 179 | * | ||
| 180 | * 0x00 is broadcast. 0x7F is unlikely to be used by any aux. | ||
| 181 | */ | ||
| 182 | result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr, | ||
| 183 | MPUREG_AUX_SLV_ADDR, 0x7F); | ||
| 184 | ERROR_CHECK(result); | ||
| 185 | /* | ||
| 186 | * 2) wait enough time for a nack to occur, then go into | ||
| 187 | * bypass mode: | ||
| 188 | */ | ||
| 189 | MLOSSleep(2); | ||
| 190 | result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr, | ||
| 191 | MPUREG_USER_CTRL, (b)); | ||
| 192 | ERROR_CHECK(result); | ||
| 193 | /* | ||
| 194 | * 3) wait for up to one MPU cycle then restore the slave | ||
| 195 | * address | ||
| 196 | */ | ||
| 197 | MLOSSleep(SAMPLING_PERIOD_US(mldl_cfg) / 1000); | ||
| 198 | result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr, | ||
| 199 | MPUREG_AUX_SLV_ADDR, | ||
| 200 | mldl_cfg->pdata-> | ||
| 201 | accel.address); | ||
| 202 | ERROR_CHECK(result); | ||
| 203 | |||
| 204 | /* | ||
| 205 | * 4) reset the ime interface | ||
| 206 | */ | ||
| 207 | #ifdef M_HW | ||
| 208 | result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr, | ||
| 209 | MPUREG_USER_CTRL, | ||
| 210 | (b | BIT_I2C_MST_RST)); | ||
| 211 | |||
| 212 | #else | ||
| 213 | result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr, | ||
| 214 | MPUREG_USER_CTRL, | ||
| 215 | (b | BIT_AUX_IF_RST)); | ||
| 216 | #endif | ||
| 217 | ERROR_CHECK(result); | ||
| 218 | MLOSSleep(2); | ||
| 219 | } | ||
| 220 | mldl_cfg->gyro_is_bypassed = enable; | ||
| 221 | |||
| 222 | return result; | ||
| 223 | } | ||
| 224 | |||
| 225 | struct tsProdRevMap { | ||
| 226 | unsigned char siliconRev; | ||
| 227 | unsigned short sensTrim; | ||
| 228 | }; | ||
| 229 | |||
| 230 | #define NUM_OF_PROD_REVS (DIM(prodRevsMap)) | ||
| 231 | |||
| 232 | /* NOTE : 'npp' is a non production part */ | ||
| 233 | #ifdef M_HW | ||
| 234 | #define OLDEST_PROD_REV_SUPPORTED 1 | ||
| 235 | static struct tsProdRevMap prodRevsMap[] = { | ||
| 236 | {0, 0}, | ||
| 237 | {MPU_SILICON_REV_A1, 131}, /* 1 A1 (npp) */ | ||
| 238 | {MPU_SILICON_REV_A1, 131}, /* 2 A1 (npp) */ | ||
| 239 | {MPU_SILICON_REV_A1, 131}, /* 3 A1 (npp) */ | ||
| 240 | {MPU_SILICON_REV_A1, 131}, /* 4 A1 (npp) */ | ||
| 241 | {MPU_SILICON_REV_A1, 131}, /* 5 A1 (npp) */ | ||
| 242 | {MPU_SILICON_REV_A1, 131}, /* 6 A1 (npp) */ | ||
| 243 | {MPU_SILICON_REV_A1, 131}, /* 7 A1 (npp) */ | ||
| 244 | {MPU_SILICON_REV_A1, 131}, /* 8 A1 (npp) */ | ||
| 245 | }; | ||
| 246 | |||
| 247 | #else /* !M_HW */ | ||
| 248 | #define OLDEST_PROD_REV_SUPPORTED 11 | ||
| 249 | |||
| 250 | static struct tsProdRevMap prodRevsMap[] = { | ||
| 251 | {0, 0}, | ||
| 252 | {MPU_SILICON_REV_A4, 131}, /* 1 A? OBSOLETED */ | ||
| 253 | {MPU_SILICON_REV_A4, 131}, /* 2 | */ | ||
| 254 | {MPU_SILICON_REV_A4, 131}, /* 3 V */ | ||
| 255 | {MPU_SILICON_REV_A4, 131}, /* 4 */ | ||
| 256 | {MPU_SILICON_REV_A4, 131}, /* 5 */ | ||
| 257 | {MPU_SILICON_REV_A4, 131}, /* 6 */ | ||
| 258 | {MPU_SILICON_REV_A4, 131}, /* 7 */ | ||
| 259 | {MPU_SILICON_REV_A4, 131}, /* 8 */ | ||
| 260 | {MPU_SILICON_REV_A4, 131}, /* 9 */ | ||
| 261 | {MPU_SILICON_REV_A4, 131}, /* 10 */ | ||
| 262 | {MPU_SILICON_REV_B1, 131}, /* 11 B1 */ | ||
| 263 | {MPU_SILICON_REV_B1, 131}, /* 12 | */ | ||
| 264 | {MPU_SILICON_REV_B1, 131}, /* 13 V */ | ||
| 265 | {MPU_SILICON_REV_B1, 131}, /* 14 B4 */ | ||
| 266 | {MPU_SILICON_REV_B4, 131}, /* 15 | */ | ||
| 267 | {MPU_SILICON_REV_B4, 131}, /* 16 V */ | ||
| 268 | {MPU_SILICON_REV_B4, 131}, /* 17 */ | ||
| 269 | {MPU_SILICON_REV_B4, 131}, /* 18 */ | ||
| 270 | {MPU_SILICON_REV_B4, 115}, /* 19 */ | ||
| 271 | {MPU_SILICON_REV_B4, 115}, /* 20 */ | ||
| 272 | {MPU_SILICON_REV_B6, 131}, /* 21 B6 (B6/A9) */ | ||
| 273 | {MPU_SILICON_REV_B4, 115}, /* 22 B4 (B7/A10) */ | ||
| 274 | {MPU_SILICON_REV_B6, 0}, /* 23 B6 (npp) */ | ||
| 275 | {MPU_SILICON_REV_B6, 0}, /* 24 | (npp) */ | ||
| 276 | {MPU_SILICON_REV_B6, 0}, /* 25 V (npp) */ | ||
| 277 | {MPU_SILICON_REV_B6, 131}, /* 26 (B6/A11) */ | ||
| 278 | }; | ||
| 279 | #endif /* !M_HW */ | ||
| 280 | |||
| 281 | /** | ||
| 282 | * @internal | ||
| 283 | * @brief Get the silicon revision ID from OTP. | ||
| 284 | * The silicon revision number is in read from OTP bank 0, | ||
| 285 | * ADDR6[7:2]. The corresponding ID is retrieved by lookup | ||
| 286 | * in a map. | ||
| 287 | * @return The silicon revision ID (0 on error). | ||
| 288 | */ | ||
| 289 | static int MLDLGetSiliconRev(struct mldl_cfg *pdata, | ||
| 290 | void *mlsl_handle) | ||
| 291 | { | ||
| 292 | int result; | ||
| 293 | unsigned char index = 0x00; | ||
| 294 | unsigned char bank = | ||
| 295 | (BIT_PRFTCH_EN | BIT_CFG_USER_BANK | MPU_MEM_OTP_BANK_0); | ||
| 296 | unsigned short memAddr = ((bank << 8) | 0x06); | ||
| 297 | |||
| 298 | result = MLSLSerialReadMem(mlsl_handle, pdata->addr, | ||
| 299 | memAddr, 1, &index); | ||
| 300 | ERROR_CHECK(result); | ||
| 301 | if (result) | ||
| 302 | return result; | ||
| 303 | index >>= 2; | ||
| 304 | |||
| 305 | /* clean the prefetch and cfg user bank bits */ | ||
| 306 | result = | ||
| 307 | MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
| 308 | MPUREG_BANK_SEL, 0); | ||
| 309 | ERROR_CHECK(result); | ||
| 310 | if (result) | ||
| 311 | return result; | ||
| 312 | |||
| 313 | if (index < OLDEST_PROD_REV_SUPPORTED || NUM_OF_PROD_REVS <= index) { | ||
| 314 | pdata->silicon_revision = 0; | ||
| 315 | pdata->trim = 0; | ||
| 316 | MPL_LOGE("Unsupported Product Revision Detected : %d\n", index); | ||
| 317 | return ML_ERROR_INVALID_MODULE; | ||
| 318 | } | ||
| 319 | |||
| 320 | pdata->silicon_revision = prodRevsMap[index].siliconRev; | ||
| 321 | pdata->trim = prodRevsMap[index].sensTrim; | ||
| 322 | |||
| 323 | if (pdata->trim == 0) { | ||
| 324 | MPL_LOGE("sensitivity trim is 0" | ||
| 325 | " - unsupported non production part.\n"); | ||
| 326 | return ML_ERROR_INVALID_MODULE; | ||
| 327 | } | ||
| 328 | |||
| 329 | return result; | ||
| 330 | } | ||
| 331 | |||
| 332 | /** | ||
| 333 | * @brief Enable / Disable the use MPU's secondary I2C interface level | ||
| 334 | * shifters. | ||
| 335 | * When enabled the secondary I2C interface to which the external | ||
| 336 | * device is connected runs at VDD voltage (main supply). | ||
| 337 | * When disabled the 2nd interface runs at VDDIO voltage. | ||
| 338 | * See the device specification for more details. | ||
| 339 | * | ||
| 340 | * @note using this API may produce unpredictable results, depending on how | ||
| 341 | * the MPU and slave device are setup on the target platform. | ||
| 342 | * Use of this API should entirely be restricted to system | ||
| 343 | * integrators. Once the correct value is found, there should be no | ||
| 344 | * need to change the level shifter at runtime. | ||
| 345 | * | ||
| 346 | * @pre Must be called after MLSerialOpen(). | ||
| 347 | * @note Typically called before MLDmpOpen(). | ||
| 348 | * | ||
| 349 | * @param[in] enable: | ||
| 350 | * 0 to run at VDDIO (default), | ||
| 351 | * 1 to run at VDD. | ||
| 352 | * | ||
| 353 | * @return ML_SUCCESS if successfull, a non-zero error code otherwise. | ||
| 354 | */ | ||
| 355 | static int MLDLSetLevelShifterBit(struct mldl_cfg *pdata, | ||
| 356 | void *mlsl_handle, | ||
| 357 | unsigned char enable) | ||
| 358 | { | ||
| 359 | #ifndef M_HW | ||
| 360 | int result; | ||
| 361 | unsigned char reg; | ||
| 362 | unsigned char mask; | ||
| 363 | unsigned char regval; | ||
| 364 | |||
| 365 | if (0 == pdata->silicon_revision) | ||
| 366 | return ML_ERROR_INVALID_PARAMETER; | ||
| 367 | |||
| 368 | /*-- on parts before B6 the VDDIO bit is bit 7 of ACCEL_BURST_ADDR -- | ||
| 369 | NOTE: this is incompatible with ST accelerometers where the VDDIO | ||
| 370 | bit MUST be set to enable ST's internal logic to autoincrement | ||
| 371 | the register address on burst reads --*/ | ||
| 372 | if ((pdata->silicon_revision & 0xf) < MPU_SILICON_REV_B6) { | ||
| 373 | reg = MPUREG_ACCEL_BURST_ADDR; | ||
| 374 | mask = 0x80; | ||
| 375 | } else { | ||
| 376 | /*-- on B6 parts the VDDIO bit was moved to FIFO_EN2 => | ||
| 377 | the mask is always 0x04 --*/ | ||
| 378 | reg = MPUREG_FIFO_EN2; | ||
| 379 | mask = 0x04; | ||
| 380 | } | ||
| 381 | |||
| 382 | result = MLSLSerialRead(mlsl_handle, pdata->addr, reg, 1, ®val); | ||
| 383 | if (result) | ||
| 384 | return result; | ||
| 385 | |||
| 386 | if (enable) | ||
| 387 | regval |= mask; | ||
| 388 | else | ||
| 389 | regval &= ~mask; | ||
| 390 | |||
| 391 | result = | ||
| 392 | MLSLSerialWriteSingle(mlsl_handle, pdata->addr, reg, regval); | ||
| 393 | |||
| 394 | return result; | ||
| 395 | #else | ||
| 396 | return ML_SUCCESS; | ||
| 397 | #endif | ||
| 398 | } | ||
| 399 | |||
| 400 | |||
| 401 | #ifdef M_HW | ||
| 402 | /** | ||
| 403 | * @internal | ||
| 404 | * @param reset 1 to reset hardware | ||
| 405 | */ | ||
| 406 | static tMLError mpu60xx_pwr_mgmt(struct mldl_cfg *pdata, | ||
| 407 | void *mlsl_handle, | ||
| 408 | unsigned char reset, | ||
| 409 | unsigned char powerselection) | ||
| 410 | { | ||
| 411 | unsigned char b; | ||
| 412 | tMLError result; | ||
| 413 | |||
| 414 | if (powerselection < 0 || powerselection > 7) | ||
| 415 | return ML_ERROR_INVALID_PARAMETER; | ||
| 416 | |||
| 417 | result = | ||
| 418 | MLSLSerialRead(mlsl_handle, pdata->addr, MPUREG_PWR_MGMT_1, 1, | ||
| 419 | &b); | ||
| 420 | ERROR_CHECK(result); | ||
| 421 | |||
| 422 | b &= ~(BITS_PWRSEL); | ||
| 423 | |||
| 424 | if (reset) { | ||
| 425 | /* Current sillicon has an errata where the reset will get | ||
| 426 | * nacked. Ignore the error code for now. */ | ||
| 427 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
| 428 | MPUREG_PWR_MGM, b | BIT_H_RESET); | ||
| 429 | #define M_HW_RESET_ERRATTA | ||
| 430 | #ifndef M_HW_RESET_ERRATTA | ||
| 431 | ERROR_CHECK(result); | ||
| 432 | #else | ||
| 433 | MLOSSleep(50); | ||
| 434 | #endif | ||
| 435 | } | ||
| 436 | |||
| 437 | b |= (powerselection << 4); | ||
| 438 | |||
| 439 | if (b & BITS_PWRSEL) | ||
| 440 | pdata->gyro_is_suspended = FALSE; | ||
| 441 | else | ||
| 442 | pdata->gyro_is_suspended = TRUE; | ||
| 443 | |||
| 444 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
| 445 | MPUREG_PWR_MGM, b); | ||
| 446 | ERROR_CHECK(result); | ||
| 447 | |||
| 448 | return ML_SUCCESS; | ||
| 449 | } | ||
| 450 | |||
| 451 | /** | ||
| 452 | * @internal | ||
| 453 | */ | ||
| 454 | static tMLError MLDLStandByGyros(struct mldl_cfg *pdata, | ||
| 455 | void *mlsl_handle, | ||
| 456 | unsigned char disable_gx, | ||
| 457 | unsigned char disable_gy, | ||
| 458 | unsigned char disable_gz) | ||
| 459 | { | ||
| 460 | unsigned char b; | ||
| 461 | tMLError result; | ||
| 462 | |||
| 463 | result = | ||
| 464 | MLSLSerialRead(mlsl_handle, pdata->addr, MPUREG_PWR_MGMT_2, 1, | ||
| 465 | &b); | ||
| 466 | ERROR_CHECK(result); | ||
| 467 | |||
| 468 | b &= ~(BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG); | ||
| 469 | b |= (disable_gx << 2 | disable_gy << 1 | disable_gz); | ||
| 470 | |||
| 471 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
| 472 | MPUREG_PWR_MGMT_2, b); | ||
| 473 | ERROR_CHECK(result); | ||
| 474 | |||
| 475 | return ML_SUCCESS; | ||
| 476 | } | ||
| 477 | |||
| 478 | /** | ||
| 479 | * @internal | ||
| 480 | */ | ||
| 481 | static tMLError MLDLStandByAccels(struct mldl_cfg *pdata, | ||
| 482 | void *mlsl_handle, | ||
| 483 | unsigned char disable_ax, | ||
| 484 | unsigned char disable_ay, | ||
| 485 | unsigned char disable_az) | ||
| 486 | { | ||
| 487 | unsigned char b; | ||
| 488 | tMLError result; | ||
| 489 | |||
| 490 | result = | ||
| 491 | MLSLSerialRead(mlsl_handle, pdata->addr, MPUREG_PWR_MGMT_2, 1, | ||
| 492 | &b); | ||
| 493 | ERROR_CHECK(result); | ||
| 494 | |||
| 495 | b &= ~(BIT_STBY_XA | BIT_STBY_YA | BIT_STBY_ZA); | ||
| 496 | b |= (disable_ax << 2 | disable_ay << 1 | disable_az); | ||
| 497 | |||
| 498 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
| 499 | MPUREG_PWR_MGMT_2, b); | ||
| 500 | ERROR_CHECK(result); | ||
| 501 | |||
| 502 | return ML_SUCCESS; | ||
| 503 | } | ||
| 504 | |||
| 505 | #else /* ! M_HW */ | ||
| 506 | |||
| 507 | /** | ||
| 508 | * @internal | ||
| 509 | * @brief This function controls the power management on the MPU device. | ||
| 510 | * The entire chip can be put to low power sleep mode, or individual | ||
| 511 | * gyros can be turned on/off. | ||
| 512 | * | ||
| 513 | * Putting the device into sleep mode depending upon the changing needs | ||
| 514 | * of the associated applications is a recommended method for reducing | ||
| 515 | * power consuption. It is a safe opearation in that sleep/wake up of | ||
| 516 | * gyros while running will not result in any interruption of data. | ||
| 517 | * | ||
| 518 | * Although it is entirely allowed to put the device into full sleep | ||
| 519 | * while running the DMP, it is not recomended because it will disrupt | ||
| 520 | * the ongoing calculations carried on inside the DMP and consequently | ||
| 521 | * the sensor fusion algorithm. Furthermore, while in sleep mode | ||
| 522 | * read & write operation from the app processor on both registers and | ||
| 523 | * memory are disabled and can only regained by restoring the MPU in | ||
| 524 | * normal power mode. | ||
| 525 | * Disabling any of the gyro axis will reduce the associated power | ||
| 526 | * consuption from the PLL but will not stop the DMP from running | ||
| 527 | * state. | ||
| 528 | * | ||
| 529 | * @param reset | ||
| 530 | * Non-zero to reset the device. Note that this setting | ||
| 531 | * is volatile and the corresponding register bit will | ||
| 532 | * clear itself right after being applied. | ||
| 533 | * @param sleep | ||
| 534 | * Non-zero to put device into full sleep. | ||
| 535 | * @param disable_gx | ||
| 536 | * Non-zero to disable gyro X. | ||
| 537 | * @param disable_gy | ||
| 538 | * Non-zero to disable gyro Y. | ||
| 539 | * @param disable_gz | ||
| 540 | * Non-zero to disable gyro Z. | ||
| 541 | * | ||
| 542 | * @return ML_SUCCESS if successfull; a non-zero error code otherwise. | ||
| 543 | */ | ||
| 544 | static int MLDLPowerMgmtMPU(struct mldl_cfg *pdata, | ||
| 545 | void *mlsl_handle, | ||
| 546 | unsigned char reset, | ||
| 547 | unsigned char sleep, | ||
| 548 | unsigned char disable_gx, | ||
| 549 | unsigned char disable_gy, | ||
| 550 | unsigned char disable_gz) | ||
| 551 | { | ||
| 552 | unsigned char b; | ||
| 553 | int result; | ||
| 554 | |||
| 555 | result = | ||
| 556 | MLSLSerialRead(mlsl_handle, pdata->addr, MPUREG_PWR_MGM, 1, | ||
| 557 | &b); | ||
| 558 | ERROR_CHECK(result); | ||
| 559 | |||
| 560 | /* If we are awake, we need to put it in bypass before resetting */ | ||
| 561 | if ((!(b & BIT_SLEEP)) && reset) | ||
| 562 | result = MLDLSetI2CBypass(pdata, mlsl_handle, 1); | ||
| 563 | |||
| 564 | /* If we are awake, we need stop the dmp sleeping */ | ||
| 565 | if ((!(b & BIT_SLEEP)) && sleep) | ||
| 566 | dmp_stop(pdata, mlsl_handle); | ||
| 567 | |||
| 568 | /* Reset if requested */ | ||
| 569 | if (reset) { | ||
| 570 | MPL_LOGV("Reset MPU3050\n"); | ||
| 571 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
| 572 | MPUREG_PWR_MGM, b | BIT_H_RESET); | ||
| 573 | ERROR_CHECK(result); | ||
| 574 | MLOSSleep(5); | ||
| 575 | pdata->gyro_needs_reset = FALSE; | ||
| 576 | /* Some chips are awake after reset and some are asleep, | ||
| 577 | * check the status */ | ||
| 578 | result = MLSLSerialRead(mlsl_handle, pdata->addr, | ||
| 579 | MPUREG_PWR_MGM, 1, &b); | ||
| 580 | ERROR_CHECK(result); | ||
| 581 | } | ||
| 582 | |||
| 583 | /* Update the suspended state just in case we return early */ | ||
| 584 | if (b & BIT_SLEEP) | ||
| 585 | pdata->gyro_is_suspended = TRUE; | ||
| 586 | else | ||
| 587 | pdata->gyro_is_suspended = FALSE; | ||
| 588 | |||
| 589 | /* if power status match requested, nothing else's left to do */ | ||
| 590 | if ((b & (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) == | ||
| 591 | (((sleep != 0) * BIT_SLEEP) | | ||
| 592 | ((disable_gx != 0) * BIT_STBY_XG) | | ||
| 593 | ((disable_gy != 0) * BIT_STBY_YG) | | ||
| 594 | ((disable_gz != 0) * BIT_STBY_ZG))) { | ||
| 595 | return ML_SUCCESS; | ||
| 596 | } | ||
| 597 | |||
| 598 | /* | ||
| 599 | * This specific transition between states needs to be reinterpreted: | ||
| 600 | * (1,1,1,1) -> (0,1,1,1) has to become | ||
| 601 | * (1,1,1,1) -> (1,0,0,0) -> (0,1,1,1) | ||
| 602 | * where | ||
| 603 | * (1,1,1,1) is (sleep=1,disable_gx=1,disable_gy=1,disable_gz=1) | ||
| 604 | */ | ||
| 605 | if ((b & (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) == | ||
| 606 | (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG) | ||
| 607 | && ((!sleep) && disable_gx && disable_gy && disable_gz)) { | ||
| 608 | result = MLDLPowerMgmtMPU(pdata, mlsl_handle, 0, 1, 0, 0, 0); | ||
| 609 | if (result) | ||
| 610 | return result; | ||
| 611 | b |= BIT_SLEEP; | ||
| 612 | b &= ~(BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG); | ||
| 613 | } | ||
| 614 | |||
| 615 | if ((b & BIT_SLEEP) != ((sleep != 0) * BIT_SLEEP)) { | ||
| 616 | if (sleep) { | ||
| 617 | result = MLDLSetI2CBypass(pdata, mlsl_handle, 1); | ||
| 618 | ERROR_CHECK(result); | ||
| 619 | b |= BIT_SLEEP; | ||
| 620 | result = | ||
| 621 | MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
| 622 | MPUREG_PWR_MGM, b); | ||
| 623 | ERROR_CHECK(result); | ||
| 624 | pdata->gyro_is_suspended = TRUE; | ||
| 625 | } else { | ||
| 626 | b &= ~BIT_SLEEP; | ||
| 627 | result = | ||
| 628 | MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
| 629 | MPUREG_PWR_MGM, b); | ||
| 630 | ERROR_CHECK(result); | ||
| 631 | pdata->gyro_is_suspended = FALSE; | ||
| 632 | MLOSSleep(5); | ||
| 633 | } | ||
| 634 | } | ||
| 635 | /*--- | ||
| 636 | WORKAROUND FOR PUTTING GYRO AXIS in STAND-BY MODE | ||
| 637 | 1) put one axis at a time in stand-by | ||
| 638 | ---*/ | ||
| 639 | if ((b & BIT_STBY_XG) != ((disable_gx != 0) * BIT_STBY_XG)) { | ||
| 640 | b ^= BIT_STBY_XG; | ||
| 641 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
| 642 | MPUREG_PWR_MGM, b); | ||
| 643 | ERROR_CHECK(result); | ||
| 644 | } | ||
| 645 | if ((b & BIT_STBY_YG) != ((disable_gy != 0) * BIT_STBY_YG)) { | ||
| 646 | b ^= BIT_STBY_YG; | ||
| 647 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
| 648 | MPUREG_PWR_MGM, b); | ||
| 649 | ERROR_CHECK(result); | ||
| 650 | } | ||
| 651 | if ((b & BIT_STBY_ZG) != ((disable_gz != 0) * BIT_STBY_ZG)) { | ||
| 652 | b ^= BIT_STBY_ZG; | ||
| 653 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
| 654 | MPUREG_PWR_MGM, b); | ||
| 655 | ERROR_CHECK(result); | ||
| 656 | } | ||
| 657 | |||
| 658 | return ML_SUCCESS; | ||
| 659 | } | ||
| 660 | #endif /* M_HW */ | ||
| 661 | |||
| 662 | |||
| 663 | void mpu_print_cfg(struct mldl_cfg *mldl_cfg) | ||
| 664 | { | ||
| 665 | struct mpu3050_platform_data *pdata = mldl_cfg->pdata; | ||
| 666 | struct ext_slave_platform_data *accel = &mldl_cfg->pdata->accel; | ||
| 667 | struct ext_slave_platform_data *compass = | ||
| 668 | &mldl_cfg->pdata->compass; | ||
| 669 | struct ext_slave_platform_data *pressure = | ||
| 670 | &mldl_cfg->pdata->pressure; | ||
| 671 | |||
| 672 | MPL_LOGD("mldl_cfg.addr = %02x\n", mldl_cfg->addr); | ||
| 673 | MPL_LOGD("mldl_cfg.int_config = %02x\n", | ||
| 674 | mldl_cfg->int_config); | ||
| 675 | MPL_LOGD("mldl_cfg.ext_sync = %02x\n", mldl_cfg->ext_sync); | ||
| 676 | MPL_LOGD("mldl_cfg.full_scale = %02x\n", | ||
| 677 | mldl_cfg->full_scale); | ||
| 678 | MPL_LOGD("mldl_cfg.lpf = %02x\n", mldl_cfg->lpf); | ||
| 679 | MPL_LOGD("mldl_cfg.clk_src = %02x\n", mldl_cfg->clk_src); | ||
| 680 | MPL_LOGD("mldl_cfg.divider = %02x\n", mldl_cfg->divider); | ||
| 681 | MPL_LOGD("mldl_cfg.dmp_enable = %02x\n", | ||
| 682 | mldl_cfg->dmp_enable); | ||
| 683 | MPL_LOGD("mldl_cfg.fifo_enable = %02x\n", | ||
| 684 | mldl_cfg->fifo_enable); | ||
| 685 | MPL_LOGD("mldl_cfg.dmp_cfg1 = %02x\n", mldl_cfg->dmp_cfg1); | ||
| 686 | MPL_LOGD("mldl_cfg.dmp_cfg2 = %02x\n", mldl_cfg->dmp_cfg2); | ||
| 687 | MPL_LOGD("mldl_cfg.offset_tc[0] = %02x\n", | ||
| 688 | mldl_cfg->offset_tc[0]); | ||
| 689 | MPL_LOGD("mldl_cfg.offset_tc[1] = %02x\n", | ||
| 690 | mldl_cfg->offset_tc[1]); | ||
| 691 | MPL_LOGD("mldl_cfg.offset_tc[2] = %02x\n", | ||
| 692 | mldl_cfg->offset_tc[2]); | ||
| 693 | MPL_LOGD("mldl_cfg.silicon_revision = %02x\n", | ||
| 694 | mldl_cfg->silicon_revision); | ||
| 695 | MPL_LOGD("mldl_cfg.product_id = %02x\n", | ||
| 696 | mldl_cfg->product_id); | ||
| 697 | MPL_LOGD("mldl_cfg.trim = %02x\n", mldl_cfg->trim); | ||
| 698 | MPL_LOGD("mldl_cfg.requested_sensors= %04lx\n", | ||
| 699 | mldl_cfg->requested_sensors); | ||
| 700 | |||
| 701 | if (mldl_cfg->accel) { | ||
| 702 | MPL_LOGD("slave_accel->suspend = %02x\n", | ||
| 703 | (int) mldl_cfg->accel->suspend); | ||
| 704 | MPL_LOGD("slave_accel->resume = %02x\n", | ||
| 705 | (int) mldl_cfg->accel->resume); | ||
| 706 | MPL_LOGD("slave_accel->read = %02x\n", | ||
| 707 | (int) mldl_cfg->accel->read); | ||
| 708 | MPL_LOGD("slave_accel->type = %02x\n", | ||
| 709 | mldl_cfg->accel->type); | ||
| 710 | MPL_LOGD("slave_accel->reg = %02x\n", | ||
| 711 | mldl_cfg->accel->reg); | ||
| 712 | MPL_LOGD("slave_accel->len = %02x\n", | ||
| 713 | mldl_cfg->accel->len); | ||
| 714 | MPL_LOGD("slave_accel->endian = %02x\n", | ||
| 715 | mldl_cfg->accel->endian); | ||
| 716 | MPL_LOGD("slave_accel->range.mantissa= %02lx\n", | ||
| 717 | mldl_cfg->accel->range.mantissa); | ||
| 718 | MPL_LOGD("slave_accel->range.fraction= %02lx\n", | ||
| 719 | mldl_cfg->accel->range.fraction); | ||
| 720 | } else { | ||
| 721 | MPL_LOGD("slave_accel = NULL\n"); | ||
| 722 | } | ||
| 723 | |||
| 724 | if (mldl_cfg->compass) { | ||
| 725 | MPL_LOGD("slave_compass->suspend = %02x\n", | ||
| 726 | (int) mldl_cfg->compass->suspend); | ||
| 727 | MPL_LOGD("slave_compass->resume = %02x\n", | ||
| 728 | (int) mldl_cfg->compass->resume); | ||
| 729 | MPL_LOGD("slave_compass->read = %02x\n", | ||
| 730 | (int) mldl_cfg->compass->read); | ||
| 731 | MPL_LOGD("slave_compass->type = %02x\n", | ||
| 732 | mldl_cfg->compass->type); | ||
| 733 | MPL_LOGD("slave_compass->reg = %02x\n", | ||
| 734 | mldl_cfg->compass->reg); | ||
| 735 | MPL_LOGD("slave_compass->len = %02x\n", | ||
| 736 | mldl_cfg->compass->len); | ||
| 737 | MPL_LOGD("slave_compass->endian = %02x\n", | ||
| 738 | mldl_cfg->compass->endian); | ||
| 739 | MPL_LOGD("slave_compass->range.mantissa= %02lx\n", | ||
| 740 | mldl_cfg->compass->range.mantissa); | ||
| 741 | MPL_LOGD("slave_compass->range.fraction= %02lx\n", | ||
| 742 | mldl_cfg->compass->range.fraction); | ||
| 743 | |||
| 744 | } else { | ||
| 745 | MPL_LOGD("slave_compass = NULL\n"); | ||
| 746 | } | ||
| 747 | |||
| 748 | if (mldl_cfg->pressure) { | ||
| 749 | MPL_LOGD("slave_pressure->suspend = %02x\n", | ||
| 750 | (int) mldl_cfg->pressure->suspend); | ||
| 751 | MPL_LOGD("slave_pressure->resume = %02x\n", | ||
| 752 | (int) mldl_cfg->pressure->resume); | ||
| 753 | MPL_LOGD("slave_pressure->read = %02x\n", | ||
| 754 | (int) mldl_cfg->pressure->read); | ||
| 755 | MPL_LOGD("slave_pressure->type = %02x\n", | ||
| 756 | mldl_cfg->pressure->type); | ||
| 757 | MPL_LOGD("slave_pressure->reg = %02x\n", | ||
| 758 | mldl_cfg->pressure->reg); | ||
| 759 | MPL_LOGD("slave_pressure->len = %02x\n", | ||
| 760 | mldl_cfg->pressure->len); | ||
| 761 | MPL_LOGD("slave_pressure->endian = %02x\n", | ||
| 762 | mldl_cfg->pressure->endian); | ||
| 763 | MPL_LOGD("slave_pressure->range.mantissa= %02lx\n", | ||
| 764 | mldl_cfg->pressure->range.mantissa); | ||
| 765 | MPL_LOGD("slave_pressure->range.fraction= %02lx\n", | ||
| 766 | mldl_cfg->pressure->range.fraction); | ||
| 767 | |||
| 768 | } else { | ||
| 769 | MPL_LOGD("slave_pressure = NULL\n"); | ||
| 770 | } | ||
| 771 | MPL_LOGD("accel->get_slave_descr = %x\n", | ||
| 772 | (unsigned int) accel->get_slave_descr); | ||
| 773 | MPL_LOGD("accel->irq = %02x\n", accel->irq); | ||
| 774 | MPL_LOGD("accel->adapt_num = %02x\n", accel->adapt_num); | ||
| 775 | MPL_LOGD("accel->bus = %02x\n", accel->bus); | ||
| 776 | MPL_LOGD("accel->address = %02x\n", accel->address); | ||
| 777 | MPL_LOGD("accel->orientation =\n" | ||
| 778 | " %2d %2d %2d\n" | ||
| 779 | " %2d %2d %2d\n" | ||
| 780 | " %2d %2d %2d\n", | ||
| 781 | accel->orientation[0], accel->orientation[1], | ||
| 782 | accel->orientation[2], accel->orientation[3], | ||
| 783 | accel->orientation[4], accel->orientation[5], | ||
| 784 | accel->orientation[6], accel->orientation[7], | ||
| 785 | accel->orientation[8]); | ||
| 786 | MPL_LOGD("compass->get_slave_descr = %x\n", | ||
| 787 | (unsigned int) compass->get_slave_descr); | ||
| 788 | MPL_LOGD("compass->irq = %02x\n", compass->irq); | ||
| 789 | MPL_LOGD("compass->adapt_num = %02x\n", compass->adapt_num); | ||
| 790 | MPL_LOGD("compass->bus = %02x\n", compass->bus); | ||
| 791 | MPL_LOGD("compass->address = %02x\n", compass->address); | ||
| 792 | MPL_LOGD("compass->orientation =\n" | ||
| 793 | " %2d %2d %2d\n" | ||
| 794 | " %2d %2d %2d\n" | ||
| 795 | " %2d %2d %2d\n", | ||
| 796 | compass->orientation[0], compass->orientation[1], | ||
| 797 | compass->orientation[2], compass->orientation[3], | ||
| 798 | compass->orientation[4], compass->orientation[5], | ||
| 799 | compass->orientation[6], compass->orientation[7], | ||
| 800 | compass->orientation[8]); | ||
| 801 | MPL_LOGD("pressure->get_slave_descr = %x\n", | ||
| 802 | (unsigned int) pressure->get_slave_descr); | ||
| 803 | MPL_LOGD("pressure->irq = %02x\n", pressure->irq); | ||
| 804 | MPL_LOGD("pressure->adapt_num = %02x\n", pressure->adapt_num); | ||
| 805 | MPL_LOGD("pressure->bus = %02x\n", pressure->bus); | ||
| 806 | MPL_LOGD("pressure->address = %02x\n", pressure->address); | ||
| 807 | MPL_LOGD("pressure->orientation =\n" | ||
| 808 | " %2d %2d %2d\n" | ||
| 809 | " %2d %2d %2d\n" | ||
| 810 | " %2d %2d %2d\n", | ||
| 811 | pressure->orientation[0], pressure->orientation[1], | ||
| 812 | pressure->orientation[2], pressure->orientation[3], | ||
| 813 | pressure->orientation[4], pressure->orientation[5], | ||
| 814 | pressure->orientation[6], pressure->orientation[7], | ||
| 815 | pressure->orientation[8]); | ||
| 816 | |||
| 817 | MPL_LOGD("pdata->int_config = %02x\n", pdata->int_config); | ||
| 818 | MPL_LOGD("pdata->level_shifter = %02x\n", | ||
| 819 | pdata->level_shifter); | ||
| 820 | MPL_LOGD("pdata->orientation =\n" | ||
| 821 | " %2d %2d %2d\n" | ||
| 822 | " %2d %2d %2d\n" | ||
| 823 | " %2d %2d %2d\n", | ||
| 824 | pdata->orientation[0], pdata->orientation[1], | ||
| 825 | pdata->orientation[2], pdata->orientation[3], | ||
| 826 | pdata->orientation[4], pdata->orientation[5], | ||
| 827 | pdata->orientation[6], pdata->orientation[7], | ||
| 828 | pdata->orientation[8]); | ||
| 829 | |||
| 830 | MPL_LOGD("Struct sizes: mldl_cfg: %d, " | ||
| 831 | "ext_slave_descr:%d, " | ||
| 832 | "mpu3050_platform_data:%d: RamOffset: %d\n", | ||
| 833 | sizeof(struct mldl_cfg), sizeof(struct ext_slave_descr), | ||
| 834 | sizeof(struct mpu3050_platform_data), | ||
| 835 | offsetof(struct mldl_cfg, ram)); | ||
| 836 | } | ||
| 837 | |||
| 838 | int mpu_set_slave(struct mldl_cfg *mldl_cfg, | ||
| 839 | void *gyro_handle, | ||
| 840 | struct ext_slave_descr *slave, | ||
| 841 | struct ext_slave_platform_data *slave_pdata) | ||
| 842 | { | ||
| 843 | int result; | ||
| 844 | unsigned char reg; | ||
| 845 | unsigned char slave_reg; | ||
| 846 | unsigned char slave_len; | ||
| 847 | unsigned char slave_endian; | ||
| 848 | unsigned char slave_address; | ||
| 849 | |||
| 850 | result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, TRUE); | ||
| 851 | |||
| 852 | if (NULL == slave || NULL == slave_pdata) { | ||
| 853 | slave_reg = 0; | ||
| 854 | slave_len = 0; | ||
| 855 | slave_endian = 0; | ||
| 856 | slave_address = 0; | ||
| 857 | } else { | ||
| 858 | slave_reg = slave->reg; | ||
| 859 | slave_len = slave->len; | ||
| 860 | slave_endian = slave->endian; | ||
| 861 | slave_address = slave_pdata->address; | ||
| 862 | } | ||
| 863 | |||
| 864 | /* Address */ | ||
| 865 | result = MLSLSerialWriteSingle(gyro_handle, | ||
| 866 | mldl_cfg->addr, | ||
| 867 | MPUREG_AUX_SLV_ADDR, | ||
| 868 | slave_address); | ||
| 869 | ERROR_CHECK(result); | ||
| 870 | /* Register */ | ||
| 871 | result = MLSLSerialRead(gyro_handle, mldl_cfg->addr, | ||
| 872 | MPUREG_ACCEL_BURST_ADDR, 1, | ||
| 873 | ®); | ||
| 874 | ERROR_CHECK(result); | ||
| 875 | reg = ((reg & 0x80) | slave_reg); | ||
| 876 | result = MLSLSerialWriteSingle(gyro_handle, | ||
| 877 | mldl_cfg->addr, | ||
| 878 | MPUREG_ACCEL_BURST_ADDR, | ||
| 879 | reg); | ||
| 880 | ERROR_CHECK(result); | ||
| 881 | |||
| 882 | #ifdef M_HW | ||
| 883 | /* Length, byte swapping, grouping & enable */ | ||
| 884 | if (slave_len > BITS_SLV_LENG) { | ||
| 885 | MPL_LOGW("Limiting slave burst read length to " | ||
| 886 | "the allowed maximum (15B, req. %d)\n", | ||
| 887 | slave_len); | ||
| 888 | slave_len = BITS_SLV_LENG; | ||
| 889 | } | ||
| 890 | reg = slave_len; | ||
| 891 | if (slave_endian == EXT_SLAVE_LITTLE_ENDIAN) | ||
| 892 | reg |= BIT_SLV_BYTE_SW; | ||
| 893 | reg |= BIT_SLV_GRP; | ||
| 894 | reg |= BIT_SLV_ENABLE; | ||
| 895 | |||
| 896 | result = MLSLSerialWriteSingle(gyro_handle, | ||
| 897 | mldl_cfg->addr, | ||
| 898 | MPUREG_I2C_SLV0_CTRL, | ||
| 899 | reg); | ||
| 900 | #else | ||
| 901 | /* Length */ | ||
| 902 | result = MLSLSerialRead(gyro_handle, mldl_cfg->addr, | ||
| 903 | MPUREG_USER_CTRL, 1, ®); | ||
| 904 | ERROR_CHECK(result); | ||
| 905 | reg = (reg & ~BIT_AUX_RD_LENG); | ||
| 906 | result = MLSLSerialWriteSingle(gyro_handle, | ||
| 907 | mldl_cfg->addr, | ||
| 908 | MPUREG_USER_CTRL, reg); | ||
| 909 | ERROR_CHECK(result); | ||
| 910 | #endif | ||
| 911 | |||
| 912 | if (slave_address) { | ||
| 913 | result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, FALSE); | ||
| 914 | ERROR_CHECK(result); | ||
| 915 | } | ||
| 916 | return result; | ||
| 917 | } | ||
| 918 | |||
| 919 | /** | ||
| 920 | * Check to see if the gyro was reset by testing a couple of registers known | ||
| 921 | * to change on reset. | ||
| 922 | * | ||
| 923 | * @param mldl_cfg mldl configuration structure | ||
| 924 | * @param gyro_handle handle used to communicate with the gyro | ||
| 925 | * | ||
| 926 | * @return ML_SUCCESS or non-zero error code | ||
| 927 | */ | ||
| 928 | static int mpu_was_reset(struct mldl_cfg *mldl_cfg, void *gyro_handle) | ||
| 929 | { | ||
| 930 | int result = ML_SUCCESS; | ||
| 931 | unsigned char reg; | ||
| 932 | |||
| 933 | result = MLSLSerialRead(gyro_handle, mldl_cfg->addr, | ||
| 934 | MPUREG_DMP_CFG_2, 1, ®); | ||
| 935 | ERROR_CHECK(result); | ||
| 936 | |||
| 937 | if (mldl_cfg->dmp_cfg2 != reg) | ||
| 938 | return TRUE; | ||
| 939 | |||
| 940 | if (0 != mldl_cfg->dmp_cfg1) | ||
| 941 | return FALSE; | ||
| 942 | |||
| 943 | result = MLSLSerialRead(gyro_handle, mldl_cfg->addr, | ||
| 944 | MPUREG_SMPLRT_DIV, 1, ®); | ||
| 945 | ERROR_CHECK(result); | ||
| 946 | if (reg != mldl_cfg->divider) | ||
| 947 | return TRUE; | ||
| 948 | |||
| 949 | if (0 != mldl_cfg->divider) | ||
| 950 | return FALSE; | ||
| 951 | |||
| 952 | /* Inconclusive assume it was reset */ | ||
| 953 | return TRUE; | ||
| 954 | } | ||
| 955 | |||
| 956 | static int gyro_resume(struct mldl_cfg *mldl_cfg, void *gyro_handle) | ||
| 957 | { | ||
| 958 | int result; | ||
| 959 | int ii; | ||
| 960 | int jj; | ||
| 961 | unsigned char reg; | ||
| 962 | unsigned char regs[7]; | ||
| 963 | |||
| 964 | /* Wake up the part */ | ||
| 965 | #ifdef M_HW | ||
| 966 | result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, RESET, | ||
| 967 | WAKE_UP); | ||
| 968 | ERROR_CHECK(result); | ||
| 969 | |||
| 970 | /* Configure the MPU */ | ||
| 971 | result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, 1); | ||
| 972 | ERROR_CHECK(result); | ||
| 973 | /* setting int_config with the propert flag BIT_BYPASS_EN | ||
| 974 | should be done by the setup functions */ | ||
| 975 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
| 976 | MPUREG_INT_PIN_CFG, | ||
| 977 | (mldl_cfg->pdata->int_config | | ||
| 978 | BIT_BYPASS_EN)); | ||
| 979 | ERROR_CHECK(result); | ||
| 980 | /* temporary: masking out higher bits to avoid switching | ||
| 981 | intelligence */ | ||
| 982 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
| 983 | MPUREG_INT_ENABLE, | ||
| 984 | (mldl_cfg->int_config)); | ||
| 985 | ERROR_CHECK(result); | ||
| 986 | #else | ||
| 987 | result = MLDLPowerMgmtMPU(mldl_cfg, gyro_handle, 0, 0, | ||
| 988 | mldl_cfg->gyro_power & BIT_STBY_XG, | ||
| 989 | mldl_cfg->gyro_power & BIT_STBY_YG, | ||
| 990 | mldl_cfg->gyro_power & BIT_STBY_ZG); | ||
| 991 | |||
| 992 | if (!mldl_cfg->gyro_needs_reset && | ||
| 993 | !mpu_was_reset(mldl_cfg, gyro_handle)) { | ||
| 994 | return ML_SUCCESS; | ||
| 995 | } | ||
| 996 | |||
| 997 | result = MLDLPowerMgmtMPU(mldl_cfg, gyro_handle, 1, 0, | ||
| 998 | mldl_cfg->gyro_power & BIT_STBY_XG, | ||
| 999 | mldl_cfg->gyro_power & BIT_STBY_YG, | ||
| 1000 | mldl_cfg->gyro_power & BIT_STBY_ZG); | ||
| 1001 | ERROR_CHECK(result); | ||
| 1002 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
| 1003 | MPUREG_INT_CFG, | ||
| 1004 | (mldl_cfg->int_config | | ||
| 1005 | mldl_cfg->pdata->int_config)); | ||
| 1006 | ERROR_CHECK(result); | ||
| 1007 | #endif | ||
| 1008 | |||
| 1009 | result = MLSLSerialRead(gyro_handle, mldl_cfg->addr, | ||
| 1010 | MPUREG_PWR_MGM, 1, ®); | ||
| 1011 | ERROR_CHECK(result); | ||
| 1012 | reg &= ~BITS_CLKSEL; | ||
| 1013 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
| 1014 | MPUREG_PWR_MGM, | ||
| 1015 | mldl_cfg->clk_src | reg); | ||
| 1016 | ERROR_CHECK(result); | ||
| 1017 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
| 1018 | MPUREG_SMPLRT_DIV, | ||
| 1019 | mldl_cfg->divider); | ||
| 1020 | ERROR_CHECK(result); | ||
| 1021 | |||
| 1022 | #ifdef M_HW | ||
| 1023 | reg = DLPF_FS_SYNC_VALUE(0, mldl_cfg->full_scale, 0); | ||
| 1024 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
| 1025 | MPUREG_GYRO_CONFIG, reg); | ||
| 1026 | reg = DLPF_FS_SYNC_VALUE(mldl_cfg->ext_sync, 0, mldl_cfg->lpf); | ||
| 1027 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
| 1028 | MPUREG_CONFIG, reg); | ||
| 1029 | #else | ||
| 1030 | reg = DLPF_FS_SYNC_VALUE(mldl_cfg->ext_sync, | ||
| 1031 | mldl_cfg->full_scale, mldl_cfg->lpf); | ||
| 1032 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
| 1033 | MPUREG_DLPF_FS_SYNC, reg); | ||
| 1034 | #endif | ||
| 1035 | ERROR_CHECK(result); | ||
| 1036 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
| 1037 | MPUREG_DMP_CFG_1, | ||
| 1038 | mldl_cfg->dmp_cfg1); | ||
| 1039 | ERROR_CHECK(result); | ||
| 1040 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
| 1041 | MPUREG_DMP_CFG_2, | ||
| 1042 | mldl_cfg->dmp_cfg2); | ||
| 1043 | ERROR_CHECK(result); | ||
| 1044 | |||
| 1045 | /* Write and verify memory */ | ||
| 1046 | for (ii = 0; ii < MPU_MEM_NUM_RAM_BANKS; ii++) { | ||
| 1047 | unsigned char read[MPU_MEM_BANK_SIZE]; | ||
| 1048 | |||
| 1049 | result = MLSLSerialWriteMem(gyro_handle, | ||
| 1050 | mldl_cfg->addr, | ||
| 1051 | ((ii << 8) | 0x00), | ||
| 1052 | MPU_MEM_BANK_SIZE, | ||
| 1053 | mldl_cfg->ram[ii]); | ||
| 1054 | ERROR_CHECK(result); | ||
| 1055 | result = MLSLSerialReadMem(gyro_handle, mldl_cfg->addr, | ||
| 1056 | ((ii << 8) | 0x00), | ||
| 1057 | MPU_MEM_BANK_SIZE, read); | ||
| 1058 | ERROR_CHECK(result); | ||
| 1059 | |||
| 1060 | #ifdef M_HW | ||
| 1061 | #define ML_SKIP_CHECK 38 | ||
| 1062 | #else | ||
| 1063 | #define ML_SKIP_CHECK 20 | ||
| 1064 | #endif | ||
| 1065 | for (jj = 0; jj < MPU_MEM_BANK_SIZE; jj++) { | ||
| 1066 | /* skip the register memory locations */ | ||
| 1067 | if (ii == 0 && jj < ML_SKIP_CHECK) | ||
| 1068 | continue; | ||
| 1069 | if (mldl_cfg->ram[ii][jj] != read[jj]) { | ||
| 1070 | result = ML_ERROR_SERIAL_WRITE; | ||
| 1071 | break; | ||
| 1072 | } | ||
| 1073 | } | ||
| 1074 | ERROR_CHECK(result); | ||
| 1075 | } | ||
| 1076 | |||
| 1077 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
| 1078 | MPUREG_XG_OFFS_TC, | ||
| 1079 | mldl_cfg->offset_tc[0]); | ||
| 1080 | ERROR_CHECK(result); | ||
| 1081 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
| 1082 | MPUREG_YG_OFFS_TC, | ||
| 1083 | mldl_cfg->offset_tc[1]); | ||
| 1084 | ERROR_CHECK(result); | ||
| 1085 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
| 1086 | MPUREG_ZG_OFFS_TC, | ||
| 1087 | mldl_cfg->offset_tc[2]); | ||
| 1088 | ERROR_CHECK(result); | ||
| 1089 | |||
| 1090 | regs[0] = MPUREG_X_OFFS_USRH; | ||
| 1091 | for (ii = 0; ii < DIM(mldl_cfg->offset); ii++) { | ||
| 1092 | regs[1 + ii * 2] = | ||
| 1093 | (unsigned char)(mldl_cfg->offset[ii] >> 8) | ||
| 1094 | & 0xff; | ||
| 1095 | regs[1 + ii * 2 + 1] = | ||
| 1096 | (unsigned char)(mldl_cfg->offset[ii] & 0xff); | ||
| 1097 | } | ||
| 1098 | result = MLSLSerialWrite(gyro_handle, mldl_cfg->addr, 7, regs); | ||
| 1099 | ERROR_CHECK(result); | ||
| 1100 | |||
| 1101 | /* Configure slaves */ | ||
| 1102 | result = MLDLSetLevelShifterBit(mldl_cfg, gyro_handle, | ||
| 1103 | mldl_cfg->pdata->level_shifter); | ||
| 1104 | ERROR_CHECK(result); | ||
| 1105 | return result; | ||
| 1106 | } | ||
| 1107 | /******************************************************************************* | ||
| 1108 | ******************************************************************************* | ||
| 1109 | * Exported functions | ||
| 1110 | ******************************************************************************* | ||
| 1111 | ******************************************************************************/ | ||
| 1112 | |||
| 1113 | /** | ||
| 1114 | * Initializes the pdata structure to defaults. | ||
| 1115 | * | ||
| 1116 | * Opens the device to read silicon revision, product id and whoami. | ||
| 1117 | * | ||
| 1118 | * @param mldl_cfg | ||
| 1119 | * The internal device configuration data structure. | ||
| 1120 | * @param mlsl_handle | ||
| 1121 | * The serial communication handle. | ||
| 1122 | * | ||
| 1123 | * @return ML_SUCCESS if silicon revision, product id and woami are supported | ||
| 1124 | * by this software. | ||
| 1125 | */ | ||
| 1126 | int mpu3050_open(struct mldl_cfg *mldl_cfg, | ||
| 1127 | void *mlsl_handle, | ||
| 1128 | void *accel_handle, | ||
| 1129 | void *compass_handle, | ||
| 1130 | void *pressure_handle) | ||
| 1131 | { | ||
| 1132 | int result; | ||
| 1133 | /* Default is Logic HIGH, pushpull, latch disabled, anyread to clear */ | ||
| 1134 | mldl_cfg->ignore_system_suspend = FALSE; | ||
| 1135 | mldl_cfg->int_config = BIT_INT_ANYRD_2CLEAR | BIT_DMP_INT_EN; | ||
| 1136 | mldl_cfg->clk_src = MPU_CLK_SEL_PLLGYROZ; | ||
| 1137 | mldl_cfg->lpf = MPU_FILTER_42HZ; | ||
| 1138 | mldl_cfg->full_scale = MPU_FS_2000DPS; | ||
| 1139 | mldl_cfg->divider = 4; | ||
| 1140 | mldl_cfg->dmp_enable = 1; | ||
| 1141 | mldl_cfg->fifo_enable = 1; | ||
| 1142 | mldl_cfg->ext_sync = 0; | ||
| 1143 | mldl_cfg->dmp_cfg1 = 0; | ||
| 1144 | mldl_cfg->dmp_cfg2 = 0; | ||
| 1145 | mldl_cfg->gyro_power = 0; | ||
| 1146 | mldl_cfg->gyro_is_bypassed = TRUE; | ||
| 1147 | mldl_cfg->dmp_is_running = FALSE; | ||
| 1148 | mldl_cfg->gyro_is_suspended = TRUE; | ||
| 1149 | mldl_cfg->accel_is_suspended = TRUE; | ||
| 1150 | mldl_cfg->compass_is_suspended = TRUE; | ||
| 1151 | mldl_cfg->pressure_is_suspended = TRUE; | ||
| 1152 | mldl_cfg->gyro_needs_reset = FALSE; | ||
| 1153 | if (mldl_cfg->addr == 0) { | ||
| 1154 | #ifdef __KERNEL__ | ||
| 1155 | return ML_ERROR_INVALID_PARAMETER; | ||
| 1156 | #else | ||
| 1157 | mldl_cfg->addr = 0x68; | ||
| 1158 | #endif | ||
| 1159 | } | ||
| 1160 | |||
| 1161 | /* | ||
| 1162 | * Reset, | ||
| 1163 | * Take the DMP out of sleep, and | ||
| 1164 | * read the product_id, sillicon rev and whoami | ||
| 1165 | */ | ||
| 1166 | #ifdef M_HW | ||
| 1167 | result = mpu60xx_pwr_mgmt(mldl_cfg, mlsl_handle, | ||
| 1168 | RESET, WAKE_UP); | ||
| 1169 | #else | ||
| 1170 | result = MLDLPowerMgmtMPU(mldl_cfg, mlsl_handle, RESET, 0, 0, 0, 0); | ||
| 1171 | #endif | ||
| 1172 | ERROR_CHECK(result); | ||
| 1173 | |||
| 1174 | result = MLDLGetSiliconRev(mldl_cfg, mlsl_handle); | ||
| 1175 | ERROR_CHECK(result); | ||
| 1176 | #ifndef M_HW | ||
| 1177 | result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr, | ||
| 1178 | MPUREG_PRODUCT_ID, 1, | ||
| 1179 | &mldl_cfg->product_id); | ||
| 1180 | ERROR_CHECK(result); | ||
| 1181 | #endif | ||
| 1182 | |||
| 1183 | /* Get the factory temperature compensation offsets */ | ||
| 1184 | result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr, | ||
| 1185 | MPUREG_XG_OFFS_TC, 1, | ||
| 1186 | &mldl_cfg->offset_tc[0]); | ||
| 1187 | ERROR_CHECK(result); | ||
| 1188 | result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr, | ||
| 1189 | MPUREG_YG_OFFS_TC, 1, | ||
| 1190 | &mldl_cfg->offset_tc[1]); | ||
| 1191 | ERROR_CHECK(result); | ||
| 1192 | result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr, | ||
| 1193 | MPUREG_ZG_OFFS_TC, 1, | ||
| 1194 | &mldl_cfg->offset_tc[2]); | ||
| 1195 | ERROR_CHECK(result); | ||
| 1196 | |||
| 1197 | /* Configure the MPU */ | ||
| 1198 | #ifdef M_HW | ||
| 1199 | result = mpu60xx_pwr_mgmt(mldl_cfg, mlsl_handle, | ||
| 1200 | FALSE, SLEEP); | ||
| 1201 | #else | ||
| 1202 | result = | ||
| 1203 | MLDLPowerMgmtMPU(mldl_cfg, mlsl_handle, 0, SLEEP, 0, 0, 0); | ||
| 1204 | #endif | ||
| 1205 | ERROR_CHECK(result); | ||
| 1206 | |||
| 1207 | if (mldl_cfg->accel && mldl_cfg->accel->init) { | ||
| 1208 | result = mldl_cfg->accel->init(accel_handle, | ||
| 1209 | mldl_cfg->accel, | ||
| 1210 | &mldl_cfg->pdata->accel); | ||
| 1211 | ERROR_CHECK(result); | ||
| 1212 | } | ||
| 1213 | |||
| 1214 | if (mldl_cfg->compass && mldl_cfg->compass->init) { | ||
| 1215 | result = mldl_cfg->compass->init(compass_handle, | ||
| 1216 | mldl_cfg->compass, | ||
| 1217 | &mldl_cfg->pdata->compass); | ||
| 1218 | if (ML_SUCCESS != result) { | ||
| 1219 | MPL_LOGE("mldl_cfg->compass->init returned %d\n", | ||
| 1220 | result); | ||
| 1221 | goto out_accel; | ||
| 1222 | } | ||
| 1223 | } | ||
| 1224 | if (mldl_cfg->pressure && mldl_cfg->pressure->init) { | ||
| 1225 | result = mldl_cfg->pressure->init(pressure_handle, | ||
| 1226 | mldl_cfg->pressure, | ||
| 1227 | &mldl_cfg->pdata->pressure); | ||
| 1228 | if (ML_SUCCESS != result) { | ||
| 1229 | MPL_LOGE("mldl_cfg->pressure->init returned %d\n", | ||
| 1230 | result); | ||
| 1231 | goto out_compass; | ||
| 1232 | } | ||
| 1233 | } | ||
| 1234 | |||
| 1235 | mldl_cfg->requested_sensors = ML_THREE_AXIS_GYRO; | ||
| 1236 | if (mldl_cfg->accel && mldl_cfg->accel->resume) | ||
| 1237 | mldl_cfg->requested_sensors |= ML_THREE_AXIS_ACCEL; | ||
| 1238 | |||
| 1239 | if (mldl_cfg->compass && mldl_cfg->compass->resume) | ||
| 1240 | mldl_cfg->requested_sensors |= ML_THREE_AXIS_COMPASS; | ||
| 1241 | |||
| 1242 | if (mldl_cfg->pressure && mldl_cfg->pressure->resume) | ||
| 1243 | mldl_cfg->requested_sensors |= ML_THREE_AXIS_PRESSURE; | ||
| 1244 | |||
| 1245 | return result; | ||
| 1246 | |||
| 1247 | out_compass: | ||
| 1248 | if (mldl_cfg->compass->init) | ||
| 1249 | mldl_cfg->compass->exit(compass_handle, | ||
| 1250 | mldl_cfg->compass, | ||
| 1251 | &mldl_cfg->pdata->compass); | ||
| 1252 | out_accel: | ||
| 1253 | if (mldl_cfg->accel->init) | ||
| 1254 | mldl_cfg->accel->exit(accel_handle, | ||
| 1255 | mldl_cfg->accel, | ||
| 1256 | &mldl_cfg->pdata->accel); | ||
| 1257 | return result; | ||
| 1258 | |||
| 1259 | } | ||
| 1260 | |||
| 1261 | /** | ||
| 1262 | * Close the mpu3050 interface | ||
| 1263 | * | ||
| 1264 | * @param mldl_cfg pointer to the configuration structure | ||
| 1265 | * @param mlsl_handle pointer to the serial layer handle | ||
| 1266 | * | ||
| 1267 | * @return ML_SUCCESS or non-zero error code | ||
| 1268 | */ | ||
| 1269 | int mpu3050_close(struct mldl_cfg *mldl_cfg, | ||
| 1270 | void *mlsl_handle, | ||
| 1271 | void *accel_handle, | ||
| 1272 | void *compass_handle, | ||
| 1273 | void *pressure_handle) | ||
| 1274 | { | ||
| 1275 | int result = ML_SUCCESS; | ||
| 1276 | int ret_result = ML_SUCCESS; | ||
| 1277 | |||
| 1278 | if (mldl_cfg->accel && mldl_cfg->accel->exit) { | ||
| 1279 | result = mldl_cfg->accel->exit(accel_handle, | ||
| 1280 | mldl_cfg->accel, | ||
| 1281 | &mldl_cfg->pdata->accel); | ||
| 1282 | if (ML_SUCCESS != result) | ||
| 1283 | MPL_LOGE("Accel exit failed %d\n", result); | ||
| 1284 | ret_result = result; | ||
| 1285 | } | ||
| 1286 | if (ML_SUCCESS == ret_result) | ||
| 1287 | ret_result = result; | ||
| 1288 | |||
| 1289 | if (mldl_cfg->compass && mldl_cfg->compass->exit) { | ||
| 1290 | result = mldl_cfg->compass->exit(compass_handle, | ||
| 1291 | mldl_cfg->compass, | ||
| 1292 | &mldl_cfg->pdata->compass); | ||
| 1293 | if (ML_SUCCESS != result) | ||
| 1294 | MPL_LOGE("Compass exit failed %d\n", result); | ||
| 1295 | } | ||
| 1296 | if (ML_SUCCESS == ret_result) | ||
| 1297 | ret_result = result; | ||
| 1298 | |||
| 1299 | if (mldl_cfg->pressure && mldl_cfg->pressure->exit) { | ||
| 1300 | result = mldl_cfg->pressure->exit(pressure_handle, | ||
| 1301 | mldl_cfg->pressure, | ||
| 1302 | &mldl_cfg->pdata->pressure); | ||
| 1303 | if (ML_SUCCESS != result) | ||
| 1304 | MPL_LOGE("Pressure exit failed %d\n", result); | ||
| 1305 | } | ||
| 1306 | if (ML_SUCCESS == ret_result) | ||
| 1307 | ret_result = result; | ||
| 1308 | |||
| 1309 | return ret_result; | ||
| 1310 | } | ||
| 1311 | |||
| 1312 | /** | ||
| 1313 | * @brief resume the MPU3050 device and all the other sensor | ||
| 1314 | * devices from their low power state. | ||
| 1315 | * | ||
| 1316 | * @param mldl_cfg | ||
| 1317 | * pointer to the configuration structure | ||
| 1318 | * @param gyro_handle | ||
| 1319 | * the main file handle to the MPU3050 device. | ||
| 1320 | * @param accel_handle | ||
| 1321 | * an handle to the accelerometer device, if sitting | ||
| 1322 | * onto a separate bus. Can match mlsl_handle if | ||
| 1323 | * the accelerometer device operates on the same | ||
| 1324 | * primary bus of MPU. | ||
| 1325 | * @param compass_handle | ||
| 1326 | * an handle to the compass device, if sitting | ||
| 1327 | * onto a separate bus. Can match mlsl_handle if | ||
| 1328 | * the compass device operates on the same | ||
| 1329 | * primary bus of MPU. | ||
| 1330 | * @param pressure_handle | ||
| 1331 | * an handle to the pressure sensor device, if sitting | ||
| 1332 | * onto a separate bus. Can match mlsl_handle if | ||
| 1333 | * the pressure sensor device operates on the same | ||
| 1334 | * primary bus of MPU. | ||
| 1335 | * @param resume_gyro | ||
| 1336 | * whether resuming the gyroscope device is | ||
| 1337 | * actually needed (if the device supports low power | ||
| 1338 | * mode of some sort). | ||
| 1339 | * @param resume_accel | ||
| 1340 | * whether resuming the accelerometer device is | ||
| 1341 | * actually needed (if the device supports low power | ||
| 1342 | * mode of some sort). | ||
| 1343 | * @param resume_compass | ||
| 1344 | * whether resuming the compass device is | ||
| 1345 | * actually needed (if the device supports low power | ||
| 1346 | * mode of some sort). | ||
| 1347 | * @param resume_pressure | ||
| 1348 | * whether resuming the pressure sensor device is | ||
| 1349 | * actually needed (if the device supports low power | ||
| 1350 | * mode of some sort). | ||
| 1351 | * @return ML_SUCCESS or a non-zero error code. | ||
| 1352 | */ | ||
| 1353 | int mpu3050_resume(struct mldl_cfg *mldl_cfg, | ||
| 1354 | void *gyro_handle, | ||
| 1355 | void *accel_handle, | ||
| 1356 | void *compass_handle, | ||
| 1357 | void *pressure_handle, | ||
| 1358 | bool resume_gyro, | ||
| 1359 | bool resume_accel, | ||
| 1360 | bool resume_compass, | ||
| 1361 | bool resume_pressure) | ||
| 1362 | { | ||
| 1363 | int result = ML_SUCCESS; | ||
| 1364 | |||
| 1365 | #ifdef CONFIG_MPU_SENSORS_DEBUG | ||
| 1366 | mpu_print_cfg(mldl_cfg); | ||
| 1367 | #endif | ||
| 1368 | |||
| 1369 | if (resume_accel && | ||
| 1370 | ((!mldl_cfg->accel) || (!mldl_cfg->accel->resume))) | ||
| 1371 | return ML_ERROR_INVALID_PARAMETER; | ||
| 1372 | if (resume_compass && | ||
| 1373 | ((!mldl_cfg->compass) || (!mldl_cfg->compass->resume))) | ||
| 1374 | return ML_ERROR_INVALID_PARAMETER; | ||
| 1375 | if (resume_pressure && | ||
| 1376 | ((!mldl_cfg->pressure) || (!mldl_cfg->pressure->resume))) | ||
| 1377 | return ML_ERROR_INVALID_PARAMETER; | ||
| 1378 | |||
| 1379 | if (resume_gyro && mldl_cfg->gyro_is_suspended) { | ||
| 1380 | result = gyro_resume(mldl_cfg, gyro_handle); | ||
| 1381 | ERROR_CHECK(result); | ||
| 1382 | } | ||
| 1383 | |||
| 1384 | if (resume_accel && mldl_cfg->accel_is_suspended) { | ||
| 1385 | if (!mldl_cfg->gyro_is_suspended && | ||
| 1386 | EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->accel.bus) { | ||
| 1387 | result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, TRUE); | ||
| 1388 | ERROR_CHECK(result); | ||
| 1389 | } | ||
| 1390 | result = mldl_cfg->accel->resume(accel_handle, | ||
| 1391 | mldl_cfg->accel, | ||
| 1392 | &mldl_cfg->pdata->accel); | ||
| 1393 | ERROR_CHECK(result); | ||
| 1394 | mldl_cfg->accel_is_suspended = FALSE; | ||
| 1395 | } | ||
| 1396 | |||
| 1397 | if (!mldl_cfg->gyro_is_suspended && !mldl_cfg->accel_is_suspended && | ||
| 1398 | EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->accel.bus) { | ||
| 1399 | result = mpu_set_slave(mldl_cfg, | ||
| 1400 | gyro_handle, | ||
| 1401 | mldl_cfg->accel, | ||
| 1402 | &mldl_cfg->pdata->accel); | ||
| 1403 | ERROR_CHECK(result); | ||
| 1404 | } | ||
| 1405 | |||
| 1406 | if (resume_compass && mldl_cfg->compass_is_suspended) { | ||
| 1407 | if (!mldl_cfg->gyro_is_suspended && | ||
| 1408 | EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->compass.bus) { | ||
| 1409 | result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, TRUE); | ||
| 1410 | ERROR_CHECK(result); | ||
| 1411 | } | ||
| 1412 | result = mldl_cfg->compass->resume(compass_handle, | ||
| 1413 | mldl_cfg->compass, | ||
| 1414 | &mldl_cfg->pdata-> | ||
| 1415 | compass); | ||
| 1416 | ERROR_CHECK(result); | ||
| 1417 | mldl_cfg->compass_is_suspended = FALSE; | ||
| 1418 | } | ||
| 1419 | |||
| 1420 | if (!mldl_cfg->gyro_is_suspended && !mldl_cfg->compass_is_suspended && | ||
| 1421 | EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->compass.bus) { | ||
| 1422 | result = mpu_set_slave(mldl_cfg, | ||
| 1423 | gyro_handle, | ||
| 1424 | mldl_cfg->compass, | ||
| 1425 | &mldl_cfg->pdata->compass); | ||
| 1426 | ERROR_CHECK(result); | ||
| 1427 | } | ||
| 1428 | |||
| 1429 | if (resume_pressure && mldl_cfg->pressure_is_suspended) { | ||
| 1430 | if (!mldl_cfg->gyro_is_suspended && | ||
| 1431 | EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->pressure.bus) { | ||
| 1432 | result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, TRUE); | ||
| 1433 | ERROR_CHECK(result); | ||
| 1434 | } | ||
| 1435 | result = mldl_cfg->pressure->resume(pressure_handle, | ||
| 1436 | mldl_cfg->pressure, | ||
| 1437 | &mldl_cfg->pdata-> | ||
| 1438 | pressure); | ||
| 1439 | ERROR_CHECK(result); | ||
| 1440 | mldl_cfg->pressure_is_suspended = FALSE; | ||
| 1441 | } | ||
| 1442 | |||
| 1443 | if (!mldl_cfg->gyro_is_suspended && !mldl_cfg->pressure_is_suspended && | ||
| 1444 | EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->pressure.bus) { | ||
| 1445 | result = mpu_set_slave(mldl_cfg, | ||
| 1446 | gyro_handle, | ||
| 1447 | mldl_cfg->pressure, | ||
| 1448 | &mldl_cfg->pdata->pressure); | ||
| 1449 | ERROR_CHECK(result); | ||
| 1450 | } | ||
| 1451 | |||
| 1452 | /* Now start */ | ||
| 1453 | if (resume_gyro) { | ||
| 1454 | result = dmp_start(mldl_cfg, gyro_handle); | ||
| 1455 | ERROR_CHECK(result); | ||
| 1456 | } | ||
| 1457 | |||
| 1458 | return result; | ||
| 1459 | } | ||
| 1460 | |||
| 1461 | /** | ||
| 1462 | * @brief suspend the MPU3050 device and all the other sensor | ||
| 1463 | * devices into their low power state. | ||
| 1464 | * @param gyro_handle | ||
| 1465 | * the main file handle to the MPU3050 device. | ||
| 1466 | * @param accel_handle | ||
| 1467 | * an handle to the accelerometer device, if sitting | ||
| 1468 | * onto a separate bus. Can match gyro_handle if | ||
| 1469 | * the accelerometer device operates on the same | ||
| 1470 | * primary bus of MPU. | ||
| 1471 | * @param compass_handle | ||
| 1472 | * an handle to the compass device, if sitting | ||
| 1473 | * onto a separate bus. Can match gyro_handle if | ||
| 1474 | * the compass device operates on the same | ||
| 1475 | * primary bus of MPU. | ||
| 1476 | * @param pressure_handle | ||
| 1477 | * an handle to the pressure sensor device, if sitting | ||
| 1478 | * onto a separate bus. Can match gyro_handle if | ||
| 1479 | * the pressure sensor device operates on the same | ||
| 1480 | * primary bus of MPU. | ||
| 1481 | * @param accel | ||
| 1482 | * whether suspending the accelerometer device is | ||
| 1483 | * actually needed (if the device supports low power | ||
| 1484 | * mode of some sort). | ||
| 1485 | * @param compass | ||
| 1486 | * whether suspending the compass device is | ||
| 1487 | * actually needed (if the device supports low power | ||
| 1488 | * mode of some sort). | ||
| 1489 | * @param pressure | ||
| 1490 | * whether suspending the pressure sensor device is | ||
| 1491 | * actually needed (if the device supports low power | ||
| 1492 | * mode of some sort). | ||
| 1493 | * @return ML_SUCCESS or a non-zero error code. | ||
| 1494 | */ | ||
| 1495 | int mpu3050_suspend(struct mldl_cfg *mldl_cfg, | ||
| 1496 | void *gyro_handle, | ||
| 1497 | void *accel_handle, | ||
| 1498 | void *compass_handle, | ||
| 1499 | void *pressure_handle, | ||
| 1500 | bool suspend_gyro, | ||
| 1501 | bool suspend_accel, | ||
| 1502 | bool suspend_compass, | ||
| 1503 | bool suspend_pressure) | ||
| 1504 | { | ||
| 1505 | int result = ML_SUCCESS; | ||
| 1506 | |||
| 1507 | if (suspend_gyro && !mldl_cfg->gyro_is_suspended) { | ||
| 1508 | #ifdef M_HW | ||
| 1509 | return ML_SUCCESS; | ||
| 1510 | /* This puts the bus into bypass mode */ | ||
| 1511 | result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, 1); | ||
| 1512 | ERROR_CHECK(result); | ||
| 1513 | result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, 0, SLEEP); | ||
| 1514 | #else | ||
| 1515 | result = MLDLPowerMgmtMPU(mldl_cfg, gyro_handle, | ||
| 1516 | 0, SLEEP, 0, 0, 0); | ||
| 1517 | #endif | ||
| 1518 | ERROR_CHECK(result); | ||
| 1519 | } | ||
| 1520 | |||
| 1521 | if (!mldl_cfg->accel_is_suspended && suspend_accel && | ||
| 1522 | mldl_cfg->accel && mldl_cfg->accel->suspend) { | ||
| 1523 | if (!mldl_cfg->gyro_is_suspended && | ||
| 1524 | EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->accel.bus) { | ||
| 1525 | result = mpu_set_slave(mldl_cfg, gyro_handle, | ||
| 1526 | NULL, NULL); | ||
| 1527 | ERROR_CHECK(result); | ||
| 1528 | } | ||
| 1529 | result = mldl_cfg->accel->suspend(accel_handle, | ||
| 1530 | mldl_cfg->accel, | ||
| 1531 | &mldl_cfg->pdata->accel); | ||
| 1532 | ERROR_CHECK(result); | ||
| 1533 | mldl_cfg->accel_is_suspended = TRUE; | ||
| 1534 | } | ||
| 1535 | |||
| 1536 | if (!mldl_cfg->compass_is_suspended && suspend_compass && | ||
| 1537 | mldl_cfg->compass && mldl_cfg->compass->suspend) { | ||
| 1538 | if (!mldl_cfg->gyro_is_suspended && | ||
| 1539 | EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->compass.bus) { | ||
| 1540 | result = mpu_set_slave(mldl_cfg, gyro_handle, | ||
| 1541 | NULL, NULL); | ||
| 1542 | ERROR_CHECK(result); | ||
| 1543 | } | ||
| 1544 | result = mldl_cfg->compass->suspend(compass_handle, | ||
| 1545 | mldl_cfg->compass, | ||
| 1546 | &mldl_cfg-> | ||
| 1547 | pdata->compass); | ||
| 1548 | ERROR_CHECK(result); | ||
| 1549 | mldl_cfg->compass_is_suspended = TRUE; | ||
| 1550 | } | ||
| 1551 | |||
| 1552 | if (!mldl_cfg->pressure_is_suspended && suspend_pressure && | ||
| 1553 | mldl_cfg->pressure && mldl_cfg->pressure->suspend) { | ||
| 1554 | if (!mldl_cfg->gyro_is_suspended && | ||
| 1555 | EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->pressure.bus) { | ||
| 1556 | result = mpu_set_slave(mldl_cfg, gyro_handle, | ||
| 1557 | NULL, NULL); | ||
| 1558 | ERROR_CHECK(result); | ||
| 1559 | } | ||
| 1560 | result = mldl_cfg->pressure->suspend(pressure_handle, | ||
| 1561 | mldl_cfg->pressure, | ||
| 1562 | &mldl_cfg-> | ||
| 1563 | pdata->pressure); | ||
| 1564 | ERROR_CHECK(result); | ||
| 1565 | mldl_cfg->pressure_is_suspended = TRUE; | ||
| 1566 | } | ||
| 1567 | return result; | ||
| 1568 | } | ||
| 1569 | |||
| 1570 | |||
| 1571 | /** | ||
| 1572 | * @brief read raw sensor data from the accelerometer device | ||
| 1573 | * in use. | ||
| 1574 | * @param mldl_cfg | ||
| 1575 | * A pointer to the struct mldl_cfg data structure. | ||
| 1576 | * @param accel_handle | ||
| 1577 | * The handle to the device the accelerometer is connected to. | ||
| 1578 | * @param data | ||
| 1579 | * a buffer to store the raw sensor data. | ||
| 1580 | * @return ML_SUCCESS if successful, a non-zero error code otherwise. | ||
| 1581 | */ | ||
| 1582 | int mpu3050_read_accel(struct mldl_cfg *mldl_cfg, | ||
| 1583 | void *accel_handle, unsigned char *data) | ||
| 1584 | { | ||
| 1585 | if (NULL != mldl_cfg->accel && NULL != mldl_cfg->accel->read) | ||
| 1586 | if ((EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->accel.bus) | ||
| 1587 | && (!mldl_cfg->gyro_is_bypassed)) | ||
| 1588 | return ML_ERROR_FEATURE_NOT_ENABLED; | ||
| 1589 | else | ||
| 1590 | return mldl_cfg->accel->read(accel_handle, | ||
| 1591 | mldl_cfg->accel, | ||
| 1592 | &mldl_cfg->pdata->accel, | ||
| 1593 | data); | ||
| 1594 | else | ||
| 1595 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 1596 | } | ||
| 1597 | |||
| 1598 | /** | ||
| 1599 | * @brief read raw sensor data from the compass device | ||
| 1600 | * in use. | ||
| 1601 | * @param mldl_cfg | ||
| 1602 | * A pointer to the struct mldl_cfg data structure. | ||
| 1603 | * @param compass_handle | ||
| 1604 | * The handle to the device the compass is connected to. | ||
| 1605 | * @param data | ||
| 1606 | * a buffer to store the raw sensor data. | ||
| 1607 | * @return ML_SUCCESS if successful, a non-zero error code otherwise. | ||
| 1608 | */ | ||
| 1609 | int mpu3050_read_compass(struct mldl_cfg *mldl_cfg, | ||
| 1610 | void *compass_handle, unsigned char *data) | ||
| 1611 | { | ||
| 1612 | if (NULL != mldl_cfg->compass && NULL != mldl_cfg->compass->read) | ||
| 1613 | if ((EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->compass.bus) | ||
| 1614 | && (!mldl_cfg->gyro_is_bypassed)) | ||
| 1615 | return ML_ERROR_FEATURE_NOT_ENABLED; | ||
| 1616 | else | ||
| 1617 | return mldl_cfg->compass->read(compass_handle, | ||
| 1618 | mldl_cfg->compass, | ||
| 1619 | &mldl_cfg->pdata->compass, | ||
| 1620 | data); | ||
| 1621 | else | ||
| 1622 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 1623 | } | ||
| 1624 | |||
| 1625 | /** | ||
| 1626 | * @brief read raw sensor data from the pressure device | ||
| 1627 | * in use. | ||
| 1628 | * @param mldl_cfg | ||
| 1629 | * A pointer to the struct mldl_cfg data structure. | ||
| 1630 | * @param pressure_handle | ||
| 1631 | * The handle to the device the pressure sensor is connected to. | ||
| 1632 | * @param data | ||
| 1633 | * a buffer to store the raw sensor data. | ||
| 1634 | * @return ML_SUCCESS if successful, a non-zero error code otherwise. | ||
| 1635 | */ | ||
| 1636 | int mpu3050_read_pressure(struct mldl_cfg *mldl_cfg, | ||
| 1637 | void *pressure_handle, unsigned char *data) | ||
| 1638 | { | ||
| 1639 | if (NULL != mldl_cfg->pressure && NULL != mldl_cfg->pressure->read) | ||
| 1640 | if ((EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->pressure.bus) | ||
| 1641 | && (!mldl_cfg->gyro_is_bypassed)) | ||
| 1642 | return ML_ERROR_FEATURE_NOT_ENABLED; | ||
| 1643 | else | ||
| 1644 | return mldl_cfg->pressure->read( | ||
| 1645 | pressure_handle, | ||
| 1646 | mldl_cfg->pressure, | ||
| 1647 | &mldl_cfg->pdata->pressure, | ||
| 1648 | data); | ||
| 1649 | else | ||
| 1650 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 1651 | } | ||
| 1652 | |||
| 1653 | int mpu3050_config_accel(struct mldl_cfg *mldl_cfg, | ||
| 1654 | void *accel_handle, | ||
| 1655 | struct ext_slave_config *data) | ||
| 1656 | { | ||
| 1657 | if (NULL != mldl_cfg->accel && NULL != mldl_cfg->accel->config) | ||
| 1658 | return mldl_cfg->accel->config(accel_handle, | ||
| 1659 | mldl_cfg->accel, | ||
| 1660 | &mldl_cfg->pdata->accel, | ||
| 1661 | data); | ||
| 1662 | else | ||
| 1663 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 1664 | |||
| 1665 | } | ||
| 1666 | |||
| 1667 | int mpu3050_config_compass(struct mldl_cfg *mldl_cfg, | ||
| 1668 | void *compass_handle, | ||
| 1669 | struct ext_slave_config *data) | ||
| 1670 | { | ||
| 1671 | if (NULL != mldl_cfg->compass && NULL != mldl_cfg->compass->config) | ||
| 1672 | return mldl_cfg->compass->config(compass_handle, | ||
| 1673 | mldl_cfg->compass, | ||
| 1674 | &mldl_cfg->pdata->compass, | ||
| 1675 | data); | ||
| 1676 | else | ||
| 1677 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 1678 | |||
| 1679 | } | ||
| 1680 | |||
| 1681 | int mpu3050_config_pressure(struct mldl_cfg *mldl_cfg, | ||
| 1682 | void *pressure_handle, | ||
| 1683 | struct ext_slave_config *data) | ||
| 1684 | { | ||
| 1685 | if (NULL != mldl_cfg->pressure && NULL != mldl_cfg->pressure->config) | ||
| 1686 | return mldl_cfg->pressure->config(pressure_handle, | ||
| 1687 | mldl_cfg->pressure, | ||
| 1688 | &mldl_cfg->pdata->pressure, | ||
| 1689 | data); | ||
| 1690 | else | ||
| 1691 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 1692 | } | ||
| 1693 | |||
| 1694 | int mpu3050_get_config_accel(struct mldl_cfg *mldl_cfg, | ||
| 1695 | void *accel_handle, | ||
| 1696 | struct ext_slave_config *data) | ||
| 1697 | { | ||
| 1698 | if (NULL != mldl_cfg->accel && NULL != mldl_cfg->accel->get_config) | ||
| 1699 | return mldl_cfg->accel->get_config(accel_handle, | ||
| 1700 | mldl_cfg->accel, | ||
| 1701 | &mldl_cfg->pdata->accel, | ||
| 1702 | data); | ||
| 1703 | else | ||
| 1704 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 1705 | |||
| 1706 | } | ||
| 1707 | |||
| 1708 | int mpu3050_get_config_compass(struct mldl_cfg *mldl_cfg, | ||
| 1709 | void *compass_handle, | ||
| 1710 | struct ext_slave_config *data) | ||
| 1711 | { | ||
| 1712 | if (NULL != mldl_cfg->compass && NULL != mldl_cfg->compass->get_config) | ||
| 1713 | return mldl_cfg->compass->get_config(compass_handle, | ||
| 1714 | mldl_cfg->compass, | ||
| 1715 | &mldl_cfg->pdata->compass, | ||
| 1716 | data); | ||
| 1717 | else | ||
| 1718 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 1719 | |||
| 1720 | } | ||
| 1721 | |||
| 1722 | int mpu3050_get_config_pressure(struct mldl_cfg *mldl_cfg, | ||
| 1723 | void *pressure_handle, | ||
| 1724 | struct ext_slave_config *data) | ||
| 1725 | { | ||
| 1726 | if (NULL != mldl_cfg->pressure && | ||
| 1727 | NULL != mldl_cfg->pressure->get_config) | ||
| 1728 | return mldl_cfg->pressure->get_config(pressure_handle, | ||
| 1729 | mldl_cfg->pressure, | ||
| 1730 | &mldl_cfg->pdata->pressure, | ||
| 1731 | data); | ||
| 1732 | else | ||
| 1733 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 1734 | } | ||
| 1735 | |||
| 1736 | |||
| 1737 | /** | ||
| 1738 | *@} | ||
| 1739 | */ | ||
diff --git a/drivers/misc/mpu3050/mldl_cfg.h b/drivers/misc/mpu3050/mldl_cfg.h new file mode 100644 index 00000000000..ad6a211c5d8 --- /dev/null +++ b/drivers/misc/mpu3050/mldl_cfg.h | |||
| @@ -0,0 +1,199 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2010 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup MLDL | ||
| 22 | * | ||
| 23 | * @{ | ||
| 24 | * @file mldl_cfg.h | ||
| 25 | * @brief The Motion Library Driver Layer Configuration header file. | ||
| 26 | */ | ||
| 27 | |||
| 28 | #ifndef __MLDL_CFG_H__ | ||
| 29 | #define __MLDL_CFG_H__ | ||
| 30 | |||
| 31 | /* ------------------ */ | ||
| 32 | /* - Include Files. - */ | ||
| 33 | /* ------------------ */ | ||
| 34 | |||
| 35 | #include "mlsl.h" | ||
| 36 | #include "mpu.h" | ||
| 37 | |||
| 38 | /* --------------------- */ | ||
| 39 | /* - Defines. - */ | ||
| 40 | /* --------------------- */ | ||
| 41 | |||
| 42 | /*************************************************************************/ | ||
| 43 | /* Sensors */ | ||
| 44 | /*************************************************************************/ | ||
| 45 | |||
| 46 | #define ML_X_GYRO (0x0001) | ||
| 47 | #define ML_Y_GYRO (0x0002) | ||
| 48 | #define ML_Z_GYRO (0x0004) | ||
| 49 | #define ML_DMP_PROCESSOR (0x0008) | ||
| 50 | |||
| 51 | #define ML_X_ACCEL (0x0010) | ||
| 52 | #define ML_Y_ACCEL (0x0020) | ||
| 53 | #define ML_Z_ACCEL (0x0040) | ||
| 54 | |||
| 55 | #define ML_X_COMPASS (0x0080) | ||
| 56 | #define ML_Y_COMPASS (0x0100) | ||
| 57 | #define ML_Z_COMPASS (0x0200) | ||
| 58 | |||
| 59 | #define ML_X_PRESSURE (0x0300) | ||
| 60 | #define ML_Y_PRESSURE (0x0800) | ||
| 61 | #define ML_Z_PRESSURE (0x1000) | ||
| 62 | |||
| 63 | #define ML_TEMPERATURE (0x2000) | ||
| 64 | #define ML_TIME (0x4000) | ||
| 65 | |||
| 66 | #define ML_THREE_AXIS_GYRO (0x000F) | ||
| 67 | #define ML_THREE_AXIS_ACCEL (0x0070) | ||
| 68 | #define ML_THREE_AXIS_COMPASS (0x0380) | ||
| 69 | #define ML_THREE_AXIS_PRESSURE (0x1C00) | ||
| 70 | |||
| 71 | #define ML_FIVE_AXIS (0x007B) | ||
| 72 | #define ML_SIX_AXIS_GYRO_ACCEL (0x007F) | ||
| 73 | #define ML_SIX_AXIS_ACCEL_COMPASS (0x03F0) | ||
| 74 | #define ML_NINE_AXIS (0x03FF) | ||
| 75 | #define ML_ALL_SENSORS (0x7FFF) | ||
| 76 | |||
| 77 | #define SAMPLING_RATE_HZ(mldl_cfg) \ | ||
| 78 | ((((((mldl_cfg)->lpf) == 0) || (((mldl_cfg)->lpf) == 7)) \ | ||
| 79 | ? (8000) \ | ||
| 80 | : (1000)) \ | ||
| 81 | / ((mldl_cfg)->divider + 1)) | ||
| 82 | |||
| 83 | #define SAMPLING_PERIOD_US(mldl_cfg) \ | ||
| 84 | ((1000000L * ((mldl_cfg)->divider + 1)) / \ | ||
| 85 | (((((mldl_cfg)->lpf) == 0) || (((mldl_cfg)->lpf) == 7)) \ | ||
| 86 | ? (8000) \ | ||
| 87 | : (1000))) | ||
| 88 | /* --------------------- */ | ||
| 89 | /* - Variables. - */ | ||
| 90 | /* --------------------- */ | ||
| 91 | |||
| 92 | /* Platform data for the MPU */ | ||
| 93 | struct mldl_cfg { | ||
| 94 | /* MPU related configuration */ | ||
| 95 | unsigned long requested_sensors; | ||
| 96 | unsigned char ignore_system_suspend; | ||
| 97 | unsigned char addr; | ||
| 98 | unsigned char int_config; | ||
| 99 | unsigned char ext_sync; | ||
| 100 | unsigned char full_scale; | ||
| 101 | unsigned char lpf; | ||
| 102 | unsigned char clk_src; | ||
| 103 | unsigned char divider; | ||
| 104 | unsigned char dmp_enable; | ||
| 105 | unsigned char fifo_enable; | ||
| 106 | unsigned char dmp_cfg1; | ||
| 107 | unsigned char dmp_cfg2; | ||
| 108 | unsigned char gyro_power; | ||
| 109 | unsigned char offset_tc[MPU_NUM_AXES]; | ||
| 110 | unsigned short offset[MPU_NUM_AXES]; | ||
| 111 | unsigned char ram[MPU_MEM_NUM_RAM_BANKS][MPU_MEM_BANK_SIZE]; | ||
| 112 | |||
| 113 | /* MPU Related stored status and info */ | ||
| 114 | unsigned char silicon_revision; | ||
| 115 | unsigned char product_id; | ||
| 116 | unsigned short trim; | ||
| 117 | |||
| 118 | /* Driver/Kernel related state information */ | ||
| 119 | int gyro_is_bypassed; | ||
| 120 | int dmp_is_running; | ||
| 121 | int gyro_is_suspended; | ||
| 122 | int accel_is_suspended; | ||
| 123 | int compass_is_suspended; | ||
| 124 | int pressure_is_suspended; | ||
| 125 | int gyro_needs_reset; | ||
| 126 | |||
| 127 | /* Slave related information */ | ||
| 128 | struct ext_slave_descr *accel; | ||
| 129 | struct ext_slave_descr *compass; | ||
| 130 | struct ext_slave_descr *pressure; | ||
| 131 | |||
| 132 | /* Platform Data */ | ||
| 133 | struct mpu3050_platform_data *pdata; | ||
| 134 | }; | ||
| 135 | |||
| 136 | |||
| 137 | int mpu3050_open(struct mldl_cfg *mldl_cfg, | ||
| 138 | void *mlsl_handle, | ||
| 139 | void *accel_handle, | ||
| 140 | void *compass_handle, | ||
| 141 | void *pressure_handle); | ||
| 142 | int mpu3050_close(struct mldl_cfg *mldl_cfg, | ||
| 143 | void *mlsl_handle, | ||
| 144 | void *accel_handle, | ||
| 145 | void *compass_handle, | ||
| 146 | void *pressure_handle); | ||
| 147 | int mpu3050_resume(struct mldl_cfg *mldl_cfg, | ||
| 148 | void *gyro_handle, | ||
| 149 | void *accel_handle, | ||
| 150 | void *compass_handle, | ||
| 151 | void *pressure_handle, | ||
| 152 | bool resume_gyro, | ||
| 153 | bool resume_accel, | ||
| 154 | bool resume_compass, | ||
| 155 | bool resume_pressure); | ||
| 156 | int mpu3050_suspend(struct mldl_cfg *mldl_cfg, | ||
| 157 | void *gyro_handle, | ||
| 158 | void *accel_handle, | ||
| 159 | void *compass_handle, | ||
| 160 | void *pressure_handle, | ||
| 161 | bool suspend_gyro, | ||
| 162 | bool suspend_accel, | ||
| 163 | bool suspend_compass, | ||
| 164 | bool suspend_pressure); | ||
| 165 | int mpu3050_read_accel(struct mldl_cfg *mldl_cfg, | ||
| 166 | void *accel_handle, | ||
| 167 | unsigned char *data); | ||
| 168 | int mpu3050_read_compass(struct mldl_cfg *mldl_cfg, | ||
| 169 | void *compass_handle, | ||
| 170 | unsigned char *data); | ||
| 171 | int mpu3050_read_pressure(struct mldl_cfg *mldl_cfg, void *mlsl_handle, | ||
| 172 | unsigned char *data); | ||
| 173 | |||
| 174 | int mpu3050_config_accel(struct mldl_cfg *mldl_cfg, | ||
| 175 | void *accel_handle, | ||
| 176 | struct ext_slave_config *data); | ||
| 177 | int mpu3050_config_compass(struct mldl_cfg *mldl_cfg, | ||
| 178 | void *compass_handle, | ||
| 179 | struct ext_slave_config *data); | ||
| 180 | int mpu3050_config_pressure(struct mldl_cfg *mldl_cfg, | ||
| 181 | void *pressure_handle, | ||
| 182 | struct ext_slave_config *data); | ||
| 183 | |||
| 184 | int mpu3050_get_config_accel(struct mldl_cfg *mldl_cfg, | ||
| 185 | void *accel_handle, | ||
| 186 | struct ext_slave_config *data); | ||
| 187 | int mpu3050_get_config_compass(struct mldl_cfg *mldl_cfg, | ||
| 188 | void *compass_handle, | ||
| 189 | struct ext_slave_config *data); | ||
| 190 | int mpu3050_get_config_pressure(struct mldl_cfg *mldl_cfg, | ||
| 191 | void *pressure_handle, | ||
| 192 | struct ext_slave_config *data); | ||
| 193 | |||
| 194 | |||
| 195 | #endif /* __MLDL_CFG_H__ */ | ||
| 196 | |||
| 197 | /** | ||
| 198 | *@} | ||
| 199 | */ | ||
diff --git a/drivers/misc/mpu3050/mlos-kernel.c b/drivers/misc/mpu3050/mlos-kernel.c new file mode 100644 index 00000000000..ced9ba4f6f3 --- /dev/null +++ b/drivers/misc/mpu3050/mlos-kernel.c | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2010 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | /** | ||
| 20 | * @defgroup | ||
| 21 | * @brief | ||
| 22 | * | ||
| 23 | * @{ | ||
| 24 | * @file mlos-kernel.c | ||
| 25 | * @brief | ||
| 26 | * | ||
| 27 | * | ||
| 28 | */ | ||
| 29 | |||
| 30 | #include "mlos.h" | ||
| 31 | #include <linux/delay.h> | ||
| 32 | #include <linux/slab.h> | ||
| 33 | |||
| 34 | void *MLOSMalloc(unsigned int numBytes) | ||
| 35 | { | ||
| 36 | return kmalloc(numBytes, GFP_KERNEL); | ||
| 37 | } | ||
| 38 | |||
| 39 | tMLError MLOSFree(void *ptr) | ||
| 40 | { | ||
| 41 | kfree(ptr); | ||
| 42 | return ML_SUCCESS; | ||
| 43 | } | ||
| 44 | |||
| 45 | tMLError MLOSCreateMutex(HANDLE *mutex) | ||
| 46 | { | ||
| 47 | /* @todo implement if needed */ | ||
| 48 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 49 | } | ||
| 50 | |||
| 51 | tMLError MLOSLockMutex(HANDLE mutex) | ||
| 52 | { | ||
| 53 | /* @todo implement if needed */ | ||
| 54 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 55 | } | ||
| 56 | |||
| 57 | tMLError MLOSUnlockMutex(HANDLE mutex) | ||
| 58 | { | ||
| 59 | /* @todo implement if needed */ | ||
| 60 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 61 | } | ||
| 62 | |||
| 63 | tMLError MLOSDestroyMutex(HANDLE handle) | ||
| 64 | { | ||
| 65 | /* @todo implement if needed */ | ||
| 66 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 67 | } | ||
| 68 | |||
| 69 | FILE *MLOSFOpen(char *filename) | ||
| 70 | { | ||
| 71 | /* @todo implement if needed */ | ||
| 72 | return NULL; | ||
| 73 | } | ||
| 74 | |||
| 75 | void MLOSFClose(FILE *fp) | ||
| 76 | { | ||
| 77 | /* @todo implement if needed */ | ||
| 78 | } | ||
| 79 | |||
| 80 | void MLOSSleep(int mSecs) | ||
| 81 | { | ||
| 82 | msleep(mSecs); | ||
| 83 | } | ||
| 84 | |||
| 85 | unsigned long MLOSGetTickCount(void) | ||
| 86 | { | ||
| 87 | /* @todo implement if needed */ | ||
| 88 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 89 | } | ||
diff --git a/drivers/misc/mpu3050/mlos.h b/drivers/misc/mpu3050/mlos.h new file mode 100644 index 00000000000..4ebb86c9fa5 --- /dev/null +++ b/drivers/misc/mpu3050/mlos.h | |||
| @@ -0,0 +1,73 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2010 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef _MLOS_H | ||
| 21 | #define _MLOS_H | ||
| 22 | |||
| 23 | #ifndef __KERNEL__ | ||
| 24 | #include <stdio.h> | ||
| 25 | #endif | ||
| 26 | |||
| 27 | #include "mltypes.h" | ||
| 28 | |||
| 29 | #ifdef __cplusplus | ||
| 30 | extern "C" { | ||
| 31 | #endif | ||
| 32 | |||
| 33 | /* ------------ */ | ||
| 34 | /* - Defines. - */ | ||
| 35 | /* ------------ */ | ||
| 36 | |||
| 37 | /* - MLOSCreateFile defines. - */ | ||
| 38 | |||
| 39 | #define MLOS_GENERIC_READ ((unsigned int)0x80000000) | ||
| 40 | #define MLOS_GENERIC_WRITE ((unsigned int)0x40000000) | ||
| 41 | #define MLOS_FILE_SHARE_READ ((unsigned int)0x00000001) | ||
| 42 | #define MLOS_FILE_SHARE_WRITE ((unsigned int)0x00000002) | ||
| 43 | #define MLOS_OPEN_EXISTING ((unsigned int)0x00000003) | ||
| 44 | |||
| 45 | /* ---------- */ | ||
| 46 | /* - Enums. - */ | ||
| 47 | /* ---------- */ | ||
| 48 | |||
| 49 | /* --------------- */ | ||
| 50 | /* - Structures. - */ | ||
| 51 | /* --------------- */ | ||
| 52 | |||
| 53 | /* --------------------- */ | ||
| 54 | /* - Function p-types. - */ | ||
| 55 | /* --------------------- */ | ||
| 56 | |||
| 57 | void *MLOSMalloc(unsigned int numBytes); | ||
| 58 | tMLError MLOSFree(void *ptr); | ||
| 59 | tMLError MLOSCreateMutex(HANDLE *mutex); | ||
| 60 | tMLError MLOSLockMutex(HANDLE mutex); | ||
| 61 | tMLError MLOSUnlockMutex(HANDLE mutex); | ||
| 62 | FILE *MLOSFOpen(char *filename); | ||
| 63 | void MLOSFClose(FILE *fp); | ||
| 64 | |||
| 65 | tMLError MLOSDestroyMutex(HANDLE handle); | ||
| 66 | |||
| 67 | void MLOSSleep(int mSecs); | ||
| 68 | unsigned long MLOSGetTickCount(void); | ||
| 69 | |||
| 70 | #ifdef __cplusplus | ||
| 71 | } | ||
| 72 | #endif | ||
| 73 | #endif /* _MLOS_H */ | ||
diff --git a/drivers/misc/mpu3050/mlsl-kernel.c b/drivers/misc/mpu3050/mlsl-kernel.c new file mode 100644 index 00000000000..cb1605131cb --- /dev/null +++ b/drivers/misc/mpu3050/mlsl-kernel.c | |||
| @@ -0,0 +1,331 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2010 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include "mlsl.h" | ||
| 21 | #include "mpu-i2c.h" | ||
| 22 | |||
| 23 | /* ------------ */ | ||
| 24 | /* - Defines. - */ | ||
| 25 | /* ------------ */ | ||
| 26 | |||
| 27 | /* ---------------------- */ | ||
| 28 | /* - Types definitions. - */ | ||
| 29 | /* ---------------------- */ | ||
| 30 | |||
| 31 | /* --------------------- */ | ||
| 32 | /* - Function p-types. - */ | ||
| 33 | /* --------------------- */ | ||
| 34 | |||
| 35 | /** | ||
| 36 | * @brief used to open the I2C or SPI serial port. | ||
| 37 | * This port is used to send and receive data to the MPU device. | ||
| 38 | * @param portNum | ||
| 39 | * The COM port number associated with the device in use. | ||
| 40 | * @return ML_SUCCESS if successful, a non-zero error code otherwise. | ||
| 41 | */ | ||
| 42 | tMLError MLSLSerialOpen(char const *port, void **sl_handle) | ||
| 43 | { | ||
| 44 | return ML_SUCCESS; | ||
| 45 | } | ||
| 46 | |||
| 47 | /** | ||
| 48 | * @brief used to reset any buffering the driver may be doing | ||
| 49 | * @return ML_SUCCESS if successful, a non-zero error code otherwise. | ||
| 50 | */ | ||
| 51 | tMLError MLSLSerialReset(void *sl_handle) | ||
| 52 | { | ||
| 53 | return ML_SUCCESS; | ||
| 54 | } | ||
| 55 | |||
| 56 | /** | ||
| 57 | * @brief used to close the I2C or SPI serial port. | ||
| 58 | * This port is used to send and receive data to the MPU device. | ||
| 59 | * @return ML_SUCCESS if successful, a non-zero error code otherwise. | ||
| 60 | */ | ||
| 61 | tMLError MLSLSerialClose(void *sl_handle) | ||
| 62 | { | ||
| 63 | return ML_SUCCESS; | ||
| 64 | } | ||
| 65 | |||
| 66 | /** | ||
| 67 | * @brief used to read a single byte of data. | ||
| 68 | * This should be sent by I2C or SPI. | ||
| 69 | * | ||
| 70 | * @param slaveAddr I2C slave address of device. | ||
| 71 | * @param registerAddr Register address to read. | ||
| 72 | * @param data Single byte of data to read. | ||
| 73 | * | ||
| 74 | * @return ML_SUCCESS if the command is successful, an error code otherwise. | ||
| 75 | */ | ||
| 76 | tMLError MLSLSerialWriteSingle(void *sl_handle, | ||
| 77 | unsigned char slaveAddr, | ||
| 78 | unsigned char registerAddr, | ||
| 79 | unsigned char data) | ||
| 80 | { | ||
| 81 | return sensor_i2c_write_register((struct i2c_adapter *) sl_handle, | ||
| 82 | slaveAddr, registerAddr, data); | ||
| 83 | } | ||
| 84 | |||
| 85 | |||
| 86 | /** | ||
| 87 | * @brief used to write multiple bytes of data from registers. | ||
| 88 | * This should be sent by I2C. | ||
| 89 | * | ||
| 90 | * @param slaveAddr I2C slave address of device. | ||
| 91 | * @param registerAddr Register address to write. | ||
| 92 | * @param length Length of burst of data. | ||
| 93 | * @param data Pointer to block of data. | ||
| 94 | * | ||
| 95 | * @return ML_SUCCESS if successful, a non-zero error code otherwise. | ||
| 96 | */ | ||
| 97 | tMLError MLSLSerialWrite(void *sl_handle, | ||
| 98 | unsigned char slaveAddr, | ||
| 99 | unsigned short length, | ||
| 100 | unsigned char const *data) | ||
| 101 | { | ||
| 102 | tMLError result; | ||
| 103 | const unsigned short dataLength = length - 1; | ||
| 104 | const unsigned char startRegAddr = data[0]; | ||
| 105 | unsigned char i2cWrite[SERIAL_MAX_TRANSFER_SIZE + 1]; | ||
| 106 | unsigned short bytesWritten = 0; | ||
| 107 | |||
| 108 | while (bytesWritten < dataLength) { | ||
| 109 | unsigned short thisLen = min(SERIAL_MAX_TRANSFER_SIZE, | ||
| 110 | dataLength - bytesWritten); | ||
| 111 | if (bytesWritten == 0) { | ||
| 112 | result = sensor_i2c_write((struct i2c_adapter *) | ||
| 113 | sl_handle, slaveAddr, | ||
| 114 | 1 + thisLen, data); | ||
| 115 | } else { | ||
| 116 | /* manually increment register addr between chunks */ | ||
| 117 | i2cWrite[0] = startRegAddr + bytesWritten; | ||
| 118 | memcpy(&i2cWrite[1], &data[1 + bytesWritten], | ||
| 119 | thisLen); | ||
| 120 | result = sensor_i2c_write((struct i2c_adapter *) | ||
| 121 | sl_handle, slaveAddr, | ||
| 122 | 1 + thisLen, i2cWrite); | ||
| 123 | } | ||
| 124 | if (ML_SUCCESS != result) | ||
| 125 | return result; | ||
| 126 | bytesWritten += thisLen; | ||
| 127 | } | ||
| 128 | return ML_SUCCESS; | ||
| 129 | } | ||
| 130 | |||
| 131 | |||
| 132 | /** | ||
| 133 | * @brief used to read multiple bytes of data from registers. | ||
| 134 | * This should be sent by I2C. | ||
| 135 | * | ||
| 136 | * @param slaveAddr I2C slave address of device. | ||
| 137 | * @param registerAddr Register address to read. | ||
| 138 | * @param length Length of burst of data. | ||
| 139 | * @param data Pointer to block of data. | ||
| 140 | * | ||
| 141 | * @return Zero if successful; an error code otherwise | ||
| 142 | */ | ||
| 143 | tMLError MLSLSerialRead(void *sl_handle, | ||
| 144 | unsigned char slaveAddr, | ||
| 145 | unsigned char registerAddr, | ||
| 146 | unsigned short length, unsigned char *data) | ||
| 147 | { | ||
| 148 | tMLError result; | ||
| 149 | unsigned short bytesRead = 0; | ||
| 150 | |||
| 151 | if (registerAddr == MPUREG_FIFO_R_W | ||
| 152 | || registerAddr == MPUREG_MEM_R_W) { | ||
| 153 | return ML_ERROR_INVALID_PARAMETER; | ||
| 154 | } | ||
| 155 | while (bytesRead < length) { | ||
| 156 | unsigned short thisLen = | ||
| 157 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytesRead); | ||
| 158 | result = | ||
| 159 | sensor_i2c_read((struct i2c_adapter *) sl_handle, | ||
| 160 | slaveAddr, registerAddr + bytesRead, | ||
| 161 | thisLen, &data[bytesRead]); | ||
| 162 | if (ML_SUCCESS != result) | ||
| 163 | return result; | ||
| 164 | bytesRead += thisLen; | ||
| 165 | } | ||
| 166 | return ML_SUCCESS; | ||
| 167 | } | ||
| 168 | |||
| 169 | |||
| 170 | /** | ||
| 171 | * @brief used to write multiple bytes of data to the memory. | ||
| 172 | * This should be sent by I2C. | ||
| 173 | * | ||
| 174 | * @param slaveAddr I2C slave address of device. | ||
| 175 | * @param memAddr The location in the memory to write to. | ||
| 176 | * @param length Length of burst data. | ||
| 177 | * @param data Pointer to block of data. | ||
| 178 | * | ||
| 179 | * @return Zero if successful; an error code otherwise | ||
| 180 | */ | ||
| 181 | tMLError MLSLSerialWriteMem(void *sl_handle, | ||
| 182 | unsigned char slaveAddr, | ||
| 183 | unsigned short memAddr, | ||
| 184 | unsigned short length, | ||
| 185 | unsigned char const *data) | ||
| 186 | { | ||
| 187 | tMLError result; | ||
| 188 | unsigned short bytesWritten = 0; | ||
| 189 | |||
| 190 | if ((memAddr & 0xFF) + length > MPU_MEM_BANK_SIZE) { | ||
| 191 | pr_err("memory read length (%d B) extends beyond its" | ||
| 192 | " limits (%d) if started at location %d\n", length, | ||
| 193 | MPU_MEM_BANK_SIZE, memAddr & 0xFF); | ||
| 194 | return ML_ERROR_INVALID_PARAMETER; | ||
| 195 | } | ||
| 196 | while (bytesWritten < length) { | ||
| 197 | unsigned short thisLen = | ||
| 198 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytesWritten); | ||
| 199 | result = | ||
| 200 | mpu_memory_write((struct i2c_adapter *) sl_handle, | ||
| 201 | slaveAddr, memAddr + bytesWritten, | ||
| 202 | thisLen, &data[bytesWritten]); | ||
| 203 | if (ML_SUCCESS != result) | ||
| 204 | return result; | ||
| 205 | bytesWritten += thisLen; | ||
| 206 | } | ||
| 207 | return ML_SUCCESS; | ||
| 208 | } | ||
| 209 | |||
| 210 | |||
| 211 | /** | ||
| 212 | * @brief used to read multiple bytes of data from the memory. | ||
| 213 | * This should be sent by I2C. | ||
| 214 | * | ||
| 215 | * @param slaveAddr I2C slave address of device. | ||
| 216 | * @param memAddr The location in the memory to read from. | ||
| 217 | * @param length Length of burst data. | ||
| 218 | * @param data Pointer to block of data. | ||
| 219 | * | ||
| 220 | * @return Zero if successful; an error code otherwise | ||
| 221 | */ | ||
| 222 | tMLError MLSLSerialReadMem(void *sl_handle, | ||
| 223 | unsigned char slaveAddr, | ||
| 224 | unsigned short memAddr, | ||
| 225 | unsigned short length, unsigned char *data) | ||
| 226 | { | ||
| 227 | tMLError result; | ||
| 228 | unsigned short bytesRead = 0; | ||
| 229 | |||
| 230 | if ((memAddr & 0xFF) + length > MPU_MEM_BANK_SIZE) { | ||
| 231 | printk | ||
| 232 | ("memory read length (%d B) extends beyond its limits (%d) " | ||
| 233 | "if started at location %d\n", length, | ||
| 234 | MPU_MEM_BANK_SIZE, memAddr & 0xFF); | ||
| 235 | return ML_ERROR_INVALID_PARAMETER; | ||
| 236 | } | ||
| 237 | while (bytesRead < length) { | ||
| 238 | unsigned short thisLen = | ||
| 239 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytesRead); | ||
| 240 | result = | ||
| 241 | mpu_memory_read((struct i2c_adapter *) sl_handle, | ||
| 242 | slaveAddr, memAddr + bytesRead, | ||
| 243 | thisLen, &data[bytesRead]); | ||
| 244 | if (ML_SUCCESS != result) | ||
| 245 | return result; | ||
| 246 | bytesRead += thisLen; | ||
| 247 | } | ||
| 248 | return ML_SUCCESS; | ||
| 249 | } | ||
| 250 | |||
| 251 | |||
| 252 | /** | ||
| 253 | * @brief used to write multiple bytes of data to the fifo. | ||
| 254 | * This should be sent by I2C. | ||
| 255 | * | ||
| 256 | * @param slaveAddr I2C slave address of device. | ||
| 257 | * @param length Length of burst of data. | ||
| 258 | * @param data Pointer to block of data. | ||
| 259 | * | ||
| 260 | * @return Zero if successful; an error code otherwise | ||
| 261 | */ | ||
| 262 | tMLError MLSLSerialWriteFifo(void *sl_handle, | ||
| 263 | unsigned char slaveAddr, | ||
| 264 | unsigned short length, | ||
| 265 | unsigned char const *data) | ||
| 266 | { | ||
| 267 | tMLError result; | ||
| 268 | unsigned char i2cWrite[SERIAL_MAX_TRANSFER_SIZE + 1]; | ||
| 269 | unsigned short bytesWritten = 0; | ||
| 270 | |||
| 271 | if (length > FIFO_HW_SIZE) { | ||
| 272 | printk(KERN_ERR | ||
| 273 | "maximum fifo write length is %d\n", FIFO_HW_SIZE); | ||
| 274 | return ML_ERROR_INVALID_PARAMETER; | ||
| 275 | } | ||
| 276 | while (bytesWritten < length) { | ||
| 277 | unsigned short thisLen = | ||
| 278 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytesWritten); | ||
| 279 | i2cWrite[0] = MPUREG_FIFO_R_W; | ||
| 280 | memcpy(&i2cWrite[1], &data[bytesWritten], thisLen); | ||
| 281 | result = sensor_i2c_write((struct i2c_adapter *) sl_handle, | ||
| 282 | slaveAddr, thisLen + 1, | ||
| 283 | i2cWrite); | ||
| 284 | if (ML_SUCCESS != result) | ||
| 285 | return result; | ||
| 286 | bytesWritten += thisLen; | ||
| 287 | } | ||
| 288 | return ML_SUCCESS; | ||
| 289 | } | ||
| 290 | |||
| 291 | |||
| 292 | /** | ||
| 293 | * @brief used to read multiple bytes of data from the fifo. | ||
| 294 | * This should be sent by I2C. | ||
| 295 | * | ||
| 296 | * @param slaveAddr I2C slave address of device. | ||
| 297 | * @param length Length of burst of data. | ||
| 298 | * @param data Pointer to block of data. | ||
| 299 | * | ||
| 300 | * @return Zero if successful; an error code otherwise | ||
| 301 | */ | ||
| 302 | tMLError MLSLSerialReadFifo(void *sl_handle, | ||
| 303 | unsigned char slaveAddr, | ||
| 304 | unsigned short length, unsigned char *data) | ||
| 305 | { | ||
| 306 | tMLError result; | ||
| 307 | unsigned short bytesRead = 0; | ||
| 308 | |||
| 309 | if (length > FIFO_HW_SIZE) { | ||
| 310 | printk(KERN_ERR | ||
| 311 | "maximum fifo read length is %d\n", FIFO_HW_SIZE); | ||
| 312 | return ML_ERROR_INVALID_PARAMETER; | ||
| 313 | } | ||
| 314 | while (bytesRead < length) { | ||
| 315 | unsigned short thisLen = | ||
| 316 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytesRead); | ||
| 317 | result = | ||
| 318 | sensor_i2c_read((struct i2c_adapter *) sl_handle, | ||
| 319 | slaveAddr, MPUREG_FIFO_R_W, thisLen, | ||
| 320 | &data[bytesRead]); | ||
| 321 | if (ML_SUCCESS != result) | ||
| 322 | return result; | ||
| 323 | bytesRead += thisLen; | ||
| 324 | } | ||
| 325 | |||
| 326 | return ML_SUCCESS; | ||
| 327 | } | ||
| 328 | |||
| 329 | /** | ||
| 330 | * @} | ||
| 331 | */ | ||
diff --git a/drivers/misc/mpu3050/mlsl.h b/drivers/misc/mpu3050/mlsl.h new file mode 100644 index 00000000000..76f69c77ba9 --- /dev/null +++ b/drivers/misc/mpu3050/mlsl.h | |||
| @@ -0,0 +1,103 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2010 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __MSSL_H__ | ||
| 21 | #define __MSSL_H__ | ||
| 22 | |||
| 23 | #include "mltypes.h" | ||
| 24 | #include "mpu.h" | ||
| 25 | |||
| 26 | #ifdef __cplusplus | ||
| 27 | extern "C" { | ||
| 28 | #endif | ||
| 29 | |||
| 30 | /* ------------ */ | ||
| 31 | /* - Defines. - */ | ||
| 32 | /* ------------ */ | ||
| 33 | |||
| 34 | /* | ||
| 35 | * NOTE : to properly support Yamaha compass reads, | ||
| 36 | * the max transfer size should be at least 9 B. | ||
| 37 | * Length in bytes, typically a power of 2 >= 2 | ||
| 38 | */ | ||
| 39 | #define SERIAL_MAX_TRANSFER_SIZE 128 | ||
| 40 | |||
| 41 | /* ---------------------- */ | ||
| 42 | /* - Types definitions. - */ | ||
| 43 | /* ---------------------- */ | ||
| 44 | |||
| 45 | /* --------------------- */ | ||
| 46 | /* - Function p-types. - */ | ||
| 47 | /* --------------------- */ | ||
| 48 | |||
| 49 | tMLError MLSLSerialOpen(char const *port, | ||
| 50 | void **sl_handle); | ||
| 51 | tMLError MLSLSerialReset(void *sl_handle); | ||
| 52 | tMLError MLSLSerialClose(void *sl_handle); | ||
| 53 | |||
| 54 | tMLError MLSLSerialWriteSingle(void *sl_handle, | ||
| 55 | unsigned char slaveAddr, | ||
| 56 | unsigned char registerAddr, | ||
| 57 | unsigned char data); | ||
| 58 | |||
| 59 | tMLError MLSLSerialRead(void *sl_handle, | ||
| 60 | unsigned char slaveAddr, | ||
| 61 | unsigned char registerAddr, | ||
| 62 | unsigned short length, | ||
| 63 | unsigned char *data); | ||
| 64 | |||
| 65 | tMLError MLSLSerialWrite(void *sl_handle, | ||
| 66 | unsigned char slaveAddr, | ||
| 67 | unsigned short length, | ||
| 68 | unsigned char const *data); | ||
| 69 | |||
| 70 | tMLError MLSLSerialReadMem(void *sl_handle, | ||
| 71 | unsigned char slaveAddr, | ||
| 72 | unsigned short memAddr, | ||
| 73 | unsigned short length, | ||
| 74 | unsigned char *data); | ||
| 75 | |||
| 76 | tMLError MLSLSerialWriteMem(void *sl_handle, | ||
| 77 | unsigned char slaveAddr, | ||
| 78 | unsigned short memAddr, | ||
| 79 | unsigned short length, | ||
| 80 | unsigned char const *data); | ||
| 81 | |||
| 82 | tMLError MLSLSerialReadFifo(void *sl_handle, | ||
| 83 | unsigned char slaveAddr, | ||
| 84 | unsigned short length, | ||
| 85 | unsigned char *data); | ||
| 86 | |||
| 87 | tMLError MLSLSerialWriteFifo(void *sl_handle, | ||
| 88 | unsigned char slaveAddr, | ||
| 89 | unsigned short length, | ||
| 90 | unsigned char const *data); | ||
| 91 | |||
| 92 | tMLError MLSLWriteCal(unsigned char *cal, unsigned int len); | ||
| 93 | tMLError MLSLReadCal(unsigned char *cal, unsigned int len); | ||
| 94 | tMLError MLSLGetCalLength(unsigned int *len); | ||
| 95 | |||
| 96 | #ifdef __cplusplus | ||
| 97 | } | ||
| 98 | #endif | ||
| 99 | |||
| 100 | /** | ||
| 101 | * @} | ||
| 102 | */ | ||
| 103 | #endif /* MLSL_H */ | ||
diff --git a/drivers/misc/mpu3050/mltypes.h b/drivers/misc/mpu3050/mltypes.h new file mode 100644 index 00000000000..d0b27fa89e7 --- /dev/null +++ b/drivers/misc/mpu3050/mltypes.h | |||
| @@ -0,0 +1,227 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2010 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @defgroup MLERROR | ||
| 22 | * @brief Motion Library - Error definitions. | ||
| 23 | * Definition of the error codes used within the MPL and returned | ||
| 24 | * to the user. | ||
| 25 | * Every function tries to return a meaningful error code basing | ||
| 26 | * on the occuring error condition. The error code is numeric. | ||
| 27 | * | ||
| 28 | * The available error codes and their associated values are: | ||
| 29 | * - (0) ML_SUCCESS | ||
| 30 | * - (1) ML_ERROR | ||
| 31 | * - (2) ML_ERROR_INVALID_PARAMETER | ||
| 32 | * - (3) ML_ERROR_FEATURE_NOT_ENABLED | ||
| 33 | * - (4) ML_ERROR_FEATURE_NOT_IMPLEMENTED | ||
| 34 | * - (6) ML_ERROR_DMP_NOT_STARTED | ||
| 35 | * - (7) ML_ERROR_DMP_STARTED | ||
| 36 | * - (8) ML_ERROR_NOT_OPENED | ||
| 37 | * - (9) ML_ERROR_OPENED | ||
| 38 | * - (10) ML_ERROR_INVALID_MODULE | ||
| 39 | * - (11) ML_ERROR_MEMORY_EXAUSTED | ||
| 40 | * - (12) ML_ERROR_DIVIDE_BY_ZERO | ||
| 41 | * - (13) ML_ERROR_ASSERTION_FAILURE | ||
| 42 | * - (14) ML_ERROR_FILE_OPEN | ||
| 43 | * - (15) ML_ERROR_FILE_READ | ||
| 44 | * - (16) ML_ERROR_FILE_WRITE | ||
| 45 | * - (20) ML_ERROR_SERIAL_CLOSED | ||
| 46 | * - (21) ML_ERROR_SERIAL_OPEN_ERROR | ||
| 47 | * - (22) ML_ERROR_SERIAL_READ | ||
| 48 | * - (23) ML_ERROR_SERIAL_WRITE | ||
| 49 | * - (24) ML_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED | ||
| 50 | * - (25) ML_ERROR_SM_TRANSITION | ||
| 51 | * - (26) ML_ERROR_SM_IMPROPER_STATE | ||
| 52 | * - (30) ML_ERROR_FIFO_OVERFLOW | ||
| 53 | * - (31) ML_ERROR_FIFO_FOOTER | ||
| 54 | * - (32) ML_ERROR_FIFO_READ_COUNT | ||
| 55 | * - (33) ML_ERROR_FIFO_READ_DATA | ||
| 56 | * - (40) ML_ERROR_MEMORY_SET | ||
| 57 | * - (50) ML_ERROR_LOG_MEMORY_ERROR | ||
| 58 | * - (51) ML_ERROR_LOG_OUTPUT_ERROR | ||
| 59 | * - (60) ML_ERROR_OS_BAD_PTR | ||
| 60 | * - (61) ML_ERROR_OS_BAD_HANDLE | ||
| 61 | * - (62) ML_ERROR_OS_CREATE_FAILED | ||
| 62 | * - (63) ML_ERROR_OS_LOCK_FAILED | ||
| 63 | * - (70) ML_ERROR_COMPASS_DATA_OVERFLOW | ||
| 64 | * - (71) ML_ERROR_COMPASS_DATA_UNDERFLOW | ||
| 65 | * - (72) ML_ERROR_COMPASS_DATA_NOT_READY | ||
| 66 | * - (73) ML_ERROR_COMPASS_DATA_ERROR | ||
| 67 | * - (75) ML_ERROR_CALIBRATION_LOAD | ||
| 68 | * - (76) ML_ERROR_CALIBRATION_STORE | ||
| 69 | * - (77) ML_ERROR_CALIBRATION_LEN | ||
| 70 | * - (78) ML_ERROR_CALIBRATION_CHECKSUM | ||
| 71 | * | ||
| 72 | * @{ | ||
| 73 | * @file mltypes.h | ||
| 74 | * @} | ||
| 75 | */ | ||
| 76 | |||
| 77 | #ifndef MLTYPES_H | ||
| 78 | #define MLTYPES_H | ||
| 79 | |||
| 80 | #ifdef __KERNEL__ | ||
| 81 | #include <linux/types.h> | ||
| 82 | #else | ||
| 83 | #include "stdint_invensense.h" | ||
| 84 | #endif | ||
| 85 | #include "log.h" | ||
| 86 | |||
| 87 | /*--------------------------- | ||
| 88 | ML Types | ||
| 89 | ---------------------------*/ | ||
| 90 | |||
| 91 | /** | ||
| 92 | * @struct tMLError mltypes.h "mltypes" | ||
| 93 | * @brief The MPL Error Code return type. | ||
| 94 | * | ||
| 95 | * @code | ||
| 96 | * typedef unsigned char tMLError; | ||
| 97 | * @endcode | ||
| 98 | */ | ||
| 99 | typedef unsigned char tMLError; | ||
| 100 | |||
| 101 | #if defined(LINUX) || defined(__KERNEL__) | ||
| 102 | typedef unsigned int HANDLE; | ||
| 103 | #endif | ||
| 104 | |||
| 105 | #ifdef __KERNEL__ | ||
| 106 | typedef HANDLE FILE; | ||
| 107 | #endif | ||
| 108 | |||
| 109 | #ifndef __cplusplus | ||
| 110 | #ifndef __KERNEL__ | ||
| 111 | typedef int_fast8_t bool; | ||
| 112 | #endif | ||
| 113 | #endif | ||
| 114 | |||
| 115 | /*--------------------------- | ||
| 116 | ML Defines | ||
| 117 | ---------------------------*/ | ||
| 118 | |||
| 119 | #ifndef NULL | ||
| 120 | #define NULL 0 | ||
| 121 | #endif | ||
| 122 | |||
| 123 | #ifndef TRUE | ||
| 124 | #define TRUE 1 | ||
| 125 | #endif | ||
| 126 | |||
| 127 | #ifndef FALSE | ||
| 128 | #define FALSE 0 | ||
| 129 | #endif | ||
| 130 | |||
| 131 | /* Dimension of an array */ | ||
| 132 | #ifndef DIM | ||
| 133 | #define DIM(array) (sizeof(array)/sizeof((array)[0])) | ||
| 134 | #endif | ||
| 135 | |||
| 136 | /* - ML Errors. - */ | ||
| 137 | #define ERROR_NAME(x) (#x) | ||
| 138 | #define ERROR_CHECK(x) \ | ||
| 139 | do { \ | ||
| 140 | if (ML_SUCCESS != x) { \ | ||
| 141 | MPL_LOGE("%s|%s|%d returning %d\n", \ | ||
| 142 | __FILE__, __func__, __LINE__, x); \ | ||
| 143 | return x; \ | ||
| 144 | } \ | ||
| 145 | } while (0) | ||
| 146 | |||
| 147 | #define ERROR_CHECK_FIRST(first, x) \ | ||
| 148 | { if (ML_SUCCESS == first) first = x; } | ||
| 149 | |||
| 150 | #define ML_SUCCESS (0) | ||
| 151 | /* Generic Error code. Proprietary Error Codes only */ | ||
| 152 | #define ML_ERROR (1) | ||
| 153 | |||
| 154 | /* Compatibility and other generic error codes */ | ||
| 155 | #define ML_ERROR_INVALID_PARAMETER (2) | ||
| 156 | #define ML_ERROR_FEATURE_NOT_ENABLED (3) | ||
| 157 | #define ML_ERROR_FEATURE_NOT_IMPLEMENTED (4) | ||
| 158 | #define ML_ERROR_DMP_NOT_STARTED (6) | ||
| 159 | #define ML_ERROR_DMP_STARTED (7) | ||
| 160 | #define ML_ERROR_NOT_OPENED (8) | ||
| 161 | #define ML_ERROR_OPENED (9) | ||
| 162 | #define ML_ERROR_INVALID_MODULE (10) | ||
| 163 | #define ML_ERROR_MEMORY_EXAUSTED (11) | ||
| 164 | #define ML_ERROR_DIVIDE_BY_ZERO (12) | ||
| 165 | #define ML_ERROR_ASSERTION_FAILURE (13) | ||
| 166 | #define ML_ERROR_FILE_OPEN (14) | ||
| 167 | #define ML_ERROR_FILE_READ (15) | ||
| 168 | #define ML_ERROR_FILE_WRITE (16) | ||
| 169 | #define ML_ERROR_INVALID_CONFIGURATION (17) | ||
| 170 | |||
| 171 | /* Serial Communication */ | ||
| 172 | #define ML_ERROR_SERIAL_CLOSED (20) | ||
| 173 | #define ML_ERROR_SERIAL_OPEN_ERROR (21) | ||
| 174 | #define ML_ERROR_SERIAL_READ (22) | ||
| 175 | #define ML_ERROR_SERIAL_WRITE (23) | ||
| 176 | #define ML_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED (24) | ||
| 177 | |||
| 178 | /* SM = State Machine */ | ||
| 179 | #define ML_ERROR_SM_TRANSITION (25) | ||
| 180 | #define ML_ERROR_SM_IMPROPER_STATE (26) | ||
| 181 | |||
| 182 | /* Fifo */ | ||
| 183 | #define ML_ERROR_FIFO_OVERFLOW (30) | ||
| 184 | #define ML_ERROR_FIFO_FOOTER (31) | ||
| 185 | #define ML_ERROR_FIFO_READ_COUNT (32) | ||
| 186 | #define ML_ERROR_FIFO_READ_DATA (33) | ||
| 187 | |||
| 188 | /* Memory & Registers, Set & Get */ | ||
| 189 | #define ML_ERROR_MEMORY_SET (40) | ||
| 190 | |||
| 191 | #define ML_ERROR_LOG_MEMORY_ERROR (50) | ||
| 192 | #define ML_ERROR_LOG_OUTPUT_ERROR (51) | ||
| 193 | |||
| 194 | /* OS interface errors */ | ||
| 195 | #define ML_ERROR_OS_BAD_PTR (60) | ||
| 196 | #define ML_ERROR_OS_BAD_HANDLE (61) | ||
| 197 | #define ML_ERROR_OS_CREATE_FAILED (62) | ||
| 198 | #define ML_ERROR_OS_LOCK_FAILED (63) | ||
| 199 | |||
| 200 | /* Compass errors */ | ||
| 201 | #define ML_ERROR_COMPASS_DATA_OVERFLOW (70) | ||
| 202 | #define ML_ERROR_COMPASS_DATA_UNDERFLOW (71) | ||
| 203 | #define ML_ERROR_COMPASS_DATA_NOT_READY (72) | ||
| 204 | #define ML_ERROR_COMPASS_DATA_ERROR (73) | ||
| 205 | |||
| 206 | /* Load/Store calibration */ | ||
| 207 | #define ML_ERROR_CALIBRATION_LOAD (75) | ||
| 208 | #define ML_ERROR_CALIBRATION_STORE (76) | ||
| 209 | #define ML_ERROR_CALIBRATION_LEN (77) | ||
| 210 | #define ML_ERROR_CALIBRATION_CHECKSUM (78) | ||
| 211 | |||
| 212 | /* Accel errors */ | ||
| 213 | #define ML_ERROR_ACCEL_DATA_OVERFLOW (79) | ||
| 214 | #define ML_ERROR_ACCEL_DATA_UNDERFLOW (80) | ||
| 215 | #define ML_ERROR_ACCEL_DATA_NOT_READY (81) | ||
| 216 | #define ML_ERROR_ACCEL_DATA_ERROR (82) | ||
| 217 | |||
| 218 | /* For Linux coding compliance */ | ||
| 219 | #ifndef __KERNEL__ | ||
| 220 | #define EXPORT_SYMBOL(x) | ||
| 221 | #endif | ||
| 222 | |||
| 223 | /*--------------------------- | ||
| 224 | p-Types | ||
| 225 | ---------------------------*/ | ||
| 226 | |||
| 227 | #endif /* MLTYPES_H */ | ||
diff --git a/drivers/misc/mpu3050/mpu-dev.c b/drivers/misc/mpu3050/mpu-dev.c new file mode 100644 index 00000000000..4dba44d4548 --- /dev/null +++ b/drivers/misc/mpu3050/mpu-dev.c | |||
| @@ -0,0 +1,1310 @@ | |||
| 1 | /* | ||
| 2 | mpu-dev.c - mpu3050 char device interface 2 $License: | ||
| 3 | |||
| 4 | Copyright (C) 1995-97 Simon G. Vogl | ||
| 5 | Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl> | ||
| 6 | Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com> | ||
| 7 | |||
| 8 | Copyright (C) 2010 InvenSense Corporation, All Rights Reserved. | ||
| 9 | |||
| 10 | This program is free software; you can redistribute it and/or modify | ||
| 11 | it under the terms of the GNU General Public License as published by | ||
| 12 | the Free Software Foundation; either version 2 of the License, or | ||
| 13 | (at your option) any later version. | ||
| 14 | |||
| 15 | This program is distributed in the hope that it will be useful, | ||
| 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 18 | GNU General Public License for more details. | ||
| 19 | |||
| 20 | You should have received a copy of the GNU General Public License | ||
| 21 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 22 | */ | ||
| 23 | #include <linux/i2c.h> | ||
| 24 | #include <linux/i2c-dev.h> | ||
| 25 | #include <linux/interrupt.h> | ||
| 26 | #include <linux/module.h> | ||
| 27 | #include <linux/moduleparam.h> | ||
| 28 | #include <linux/kernel.h> | ||
| 29 | #include <linux/init.h> | ||
| 30 | #include <linux/stat.h> | ||
| 31 | #include <linux/irq.h> | ||
| 32 | #include <linux/gpio.h> | ||
| 33 | #include <linux/signal.h> | ||
| 34 | #include <linux/miscdevice.h> | ||
| 35 | #include <linux/slab.h> | ||
| 36 | #include <linux/version.h> | ||
| 37 | #include <linux/pm.h> | ||
| 38 | #include <linux/mutex.h> | ||
| 39 | #include <linux/suspend.h> | ||
| 40 | #include <linux/poll.h> | ||
| 41 | #ifdef CONFIG_HAS_EARLYSUSPEND | ||
| 42 | #include <linux/earlysuspend.h> | ||
| 43 | #endif | ||
| 44 | |||
| 45 | #include <linux/errno.h> | ||
| 46 | #include <linux/fs.h> | ||
| 47 | #include <linux/mm.h> | ||
| 48 | #include <linux/sched.h> | ||
| 49 | #include <linux/wait.h> | ||
| 50 | #include <linux/uaccess.h> | ||
| 51 | #include <linux/io.h> | ||
| 52 | |||
| 53 | #include "mpuirq.h" | ||
| 54 | #include "slaveirq.h" | ||
| 55 | #include "mlsl.h" | ||
| 56 | #include "mpu-i2c.h" | ||
| 57 | #include "mldl_cfg.h" | ||
| 58 | #include "mpu.h" | ||
| 59 | |||
| 60 | #define MPU3050_EARLY_SUSPEND_IN_DRIVER 0 | ||
| 61 | |||
| 62 | /* Platform data for the MPU */ | ||
| 63 | struct mpu_private_data { | ||
| 64 | struct mldl_cfg mldl_cfg; | ||
| 65 | |||
| 66 | #ifdef CONFIG_HAS_EARLYSUSPEND | ||
| 67 | struct early_suspend early_suspend; | ||
| 68 | #endif | ||
| 69 | struct mutex mutex; | ||
| 70 | wait_queue_head_t mpu_event_wait; | ||
| 71 | struct completion completion; | ||
| 72 | struct timer_list timeout; | ||
| 73 | struct notifier_block nb; | ||
| 74 | struct mpuirq_data mpu_pm_event; | ||
| 75 | int response_timeout; /* In seconds */ | ||
| 76 | unsigned long event; | ||
| 77 | int pid; | ||
| 78 | }; | ||
| 79 | |||
| 80 | static struct i2c_client *this_client; | ||
| 81 | |||
| 82 | |||
| 83 | static void | ||
| 84 | mpu_pm_timeout(u_long data) | ||
| 85 | { | ||
| 86 | struct mpu_private_data *mpu = (struct mpu_private_data *) data; | ||
| 87 | dev_dbg(&this_client->adapter->dev, "%s\n", __func__); | ||
| 88 | complete(&mpu->completion); | ||
| 89 | } | ||
| 90 | |||
| 91 | static int mpu_pm_notifier_callback(struct notifier_block *nb, | ||
| 92 | unsigned long event, | ||
| 93 | void *unused) | ||
| 94 | { | ||
| 95 | struct mpu_private_data *mpu = | ||
| 96 | container_of(nb, struct mpu_private_data, nb); | ||
| 97 | struct timeval event_time; | ||
| 98 | dev_dbg(&this_client->adapter->dev, "%s: %ld\n", __func__, event); | ||
| 99 | |||
| 100 | /* Prevent the file handle from being closed before we initialize | ||
| 101 | the completion event */ | ||
| 102 | mutex_lock(&mpu->mutex); | ||
| 103 | if (!(mpu->pid) || | ||
| 104 | (event != PM_SUSPEND_PREPARE && event != PM_POST_SUSPEND)) { | ||
| 105 | mutex_unlock(&mpu->mutex); | ||
| 106 | return NOTIFY_OK; | ||
| 107 | } | ||
| 108 | |||
| 109 | do_gettimeofday(&event_time); | ||
| 110 | mpu->mpu_pm_event.interruptcount++; | ||
| 111 | mpu->mpu_pm_event.irqtime = | ||
| 112 | (((long long) event_time.tv_sec) << 32) + | ||
| 113 | event_time.tv_usec; | ||
| 114 | mpu->mpu_pm_event.data_type = MPUIRQ_DATA_TYPE_PM_EVENT; | ||
| 115 | mpu->mpu_pm_event.data_size = sizeof(unsigned long); | ||
| 116 | mpu->mpu_pm_event.data = &mpu->event; | ||
| 117 | |||
| 118 | if (event == PM_SUSPEND_PREPARE) | ||
| 119 | mpu->event = MPU_PM_EVENT_SUSPEND_PREPARE; | ||
| 120 | if (event == PM_POST_SUSPEND) | ||
| 121 | mpu->event = MPU_PM_EVENT_POST_SUSPEND; | ||
| 122 | |||
| 123 | if (mpu->response_timeout > 0) { | ||
| 124 | mpu->timeout.expires = jiffies + mpu->response_timeout * HZ; | ||
| 125 | add_timer(&mpu->timeout); | ||
| 126 | } | ||
| 127 | INIT_COMPLETION(mpu->completion); | ||
| 128 | mutex_unlock(&mpu->mutex); | ||
| 129 | |||
| 130 | wake_up_interruptible(&mpu->mpu_event_wait); | ||
| 131 | wait_for_completion(&mpu->completion); | ||
| 132 | del_timer_sync(&mpu->timeout); | ||
| 133 | dev_dbg(&this_client->adapter->dev, "%s: %ld DONE\n", __func__, event); | ||
| 134 | return NOTIFY_OK; | ||
| 135 | } | ||
| 136 | |||
| 137 | static int mpu_open(struct inode *inode, struct file *file) | ||
| 138 | { | ||
| 139 | struct mpu_private_data *mpu = | ||
| 140 | (struct mpu_private_data *) i2c_get_clientdata(this_client); | ||
| 141 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 142 | int result; | ||
| 143 | dev_dbg(&this_client->adapter->dev, "mpu_open\n"); | ||
| 144 | dev_dbg(&this_client->adapter->dev, "current->pid %d\n", | ||
| 145 | current->pid); | ||
| 146 | mpu->pid = current->pid; | ||
| 147 | file->private_data = this_client; | ||
| 148 | /* we could do some checking on the flags supplied by "open" */ | ||
| 149 | /* i.e. O_NONBLOCK */ | ||
| 150 | /* -> set some flag to disable interruptible_sleep_on in mpu_read */ | ||
| 151 | |||
| 152 | /* Reset the sensors to the default */ | ||
| 153 | result = mutex_lock_interruptible(&mpu->mutex); | ||
| 154 | if (result) { | ||
| 155 | dev_err(&this_client->adapter->dev, | ||
| 156 | "%s: mutex_lock_interruptible returned %d\n", | ||
| 157 | __func__, result); | ||
| 158 | return result; | ||
| 159 | } | ||
| 160 | mldl_cfg->requested_sensors = ML_THREE_AXIS_GYRO; | ||
| 161 | if (mldl_cfg->accel && mldl_cfg->accel->resume) | ||
| 162 | mldl_cfg->requested_sensors |= ML_THREE_AXIS_ACCEL; | ||
| 163 | |||
| 164 | if (mldl_cfg->compass && mldl_cfg->compass->resume) | ||
| 165 | mldl_cfg->requested_sensors |= ML_THREE_AXIS_COMPASS; | ||
| 166 | |||
| 167 | if (mldl_cfg->pressure && mldl_cfg->pressure->resume) | ||
| 168 | mldl_cfg->requested_sensors |= ML_THREE_AXIS_PRESSURE; | ||
| 169 | mutex_unlock(&mpu->mutex); | ||
| 170 | return 0; | ||
| 171 | } | ||
| 172 | |||
| 173 | /* close function - called when the "file" /dev/mpu is closed in userspace */ | ||
| 174 | static int mpu_release(struct inode *inode, struct file *file) | ||
| 175 | { | ||
| 176 | struct i2c_client *client = | ||
| 177 | (struct i2c_client *) file->private_data; | ||
| 178 | struct mpu_private_data *mpu = | ||
| 179 | (struct mpu_private_data *) i2c_get_clientdata(client); | ||
| 180 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 181 | struct i2c_adapter *accel_adapter; | ||
| 182 | struct i2c_adapter *compass_adapter; | ||
| 183 | struct i2c_adapter *pressure_adapter; | ||
| 184 | int result = 0; | ||
| 185 | |||
| 186 | accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num); | ||
| 187 | compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num); | ||
| 188 | pressure_adapter = i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num); | ||
| 189 | |||
| 190 | mutex_lock(&mpu->mutex); | ||
| 191 | result = mpu3050_suspend(mldl_cfg, client->adapter, | ||
| 192 | accel_adapter, compass_adapter, | ||
| 193 | pressure_adapter, | ||
| 194 | TRUE, TRUE, TRUE, TRUE); | ||
| 195 | mpu->pid = 0; | ||
| 196 | mutex_unlock(&mpu->mutex); | ||
| 197 | complete(&mpu->completion); | ||
| 198 | dev_dbg(&this_client->adapter->dev, "mpu_release\n"); | ||
| 199 | return result; | ||
| 200 | } | ||
| 201 | |||
| 202 | /* read function called when from /dev/mpu is read. Read from the FIFO */ | ||
| 203 | static ssize_t mpu_read(struct file *file, | ||
| 204 | char __user *buf, size_t count, loff_t *offset) | ||
| 205 | { | ||
| 206 | struct mpuirq_data local_mpu_pm_event; | ||
| 207 | struct i2c_client *client = | ||
| 208 | (struct i2c_client *) file->private_data; | ||
| 209 | struct mpu_private_data *mpu = | ||
| 210 | (struct mpu_private_data *) i2c_get_clientdata(client); | ||
| 211 | size_t len = sizeof(mpu->mpu_pm_event) + sizeof(unsigned long); | ||
| 212 | int err; | ||
| 213 | |||
| 214 | if (!mpu->event && (!(file->f_flags & O_NONBLOCK))) | ||
| 215 | wait_event_interruptible(mpu->mpu_event_wait, mpu->event); | ||
| 216 | |||
| 217 | if (!mpu->event || NULL == buf | ||
| 218 | || count < sizeof(mpu->mpu_pm_event) + sizeof(unsigned long)) | ||
| 219 | return 0; | ||
| 220 | |||
| 221 | err = copy_from_user(&local_mpu_pm_event, buf, | ||
| 222 | sizeof(mpu->mpu_pm_event)); | ||
| 223 | if (err != 0) { | ||
| 224 | dev_err(&this_client->adapter->dev, | ||
| 225 | "Copy from user returned %d\n", err); | ||
| 226 | return -EFAULT; | ||
| 227 | } | ||
| 228 | |||
| 229 | mpu->mpu_pm_event.data = local_mpu_pm_event.data; | ||
| 230 | err = copy_to_user((unsigned long __user *)local_mpu_pm_event.data, | ||
| 231 | &mpu->event, | ||
| 232 | sizeof(mpu->event)); | ||
| 233 | if (err != 0) { | ||
| 234 | dev_err(&this_client->adapter->dev, | ||
| 235 | "Copy to user returned %d\n", err); | ||
| 236 | return -EFAULT; | ||
| 237 | } | ||
| 238 | err = copy_to_user(buf, &mpu->mpu_pm_event, sizeof(mpu->mpu_pm_event)); | ||
| 239 | if (err != 0) { | ||
| 240 | dev_err(&this_client->adapter->dev, | ||
| 241 | "Copy to user returned %d\n", err); | ||
| 242 | return -EFAULT; | ||
| 243 | } | ||
| 244 | mpu->event = 0; | ||
| 245 | return len; | ||
| 246 | } | ||
| 247 | |||
| 248 | static unsigned int mpu_poll(struct file *file, struct poll_table_struct *poll) | ||
| 249 | { | ||
| 250 | struct i2c_client *client = | ||
| 251 | (struct i2c_client *) file->private_data; | ||
| 252 | struct mpu_private_data *mpu = | ||
| 253 | (struct mpu_private_data *) i2c_get_clientdata(client); | ||
| 254 | int mask = 0; | ||
| 255 | |||
| 256 | poll_wait(file, &mpu->mpu_event_wait, poll); | ||
| 257 | if (mpu->event) | ||
| 258 | mask |= POLLIN | POLLRDNORM; | ||
| 259 | return mask; | ||
| 260 | } | ||
| 261 | |||
| 262 | static int | ||
| 263 | mpu_ioctl_set_mpu_pdata(struct i2c_client *client, unsigned long arg) | ||
| 264 | { | ||
| 265 | int ii; | ||
| 266 | struct mpu_private_data *mpu = | ||
| 267 | (struct mpu_private_data *) i2c_get_clientdata(client); | ||
| 268 | struct mpu3050_platform_data *pdata = mpu->mldl_cfg.pdata; | ||
| 269 | struct mpu3050_platform_data local_pdata; | ||
| 270 | |||
| 271 | if (copy_from_user(&local_pdata, (unsigned char __user *) arg, | ||
| 272 | sizeof(local_pdata))) | ||
| 273 | return -EFAULT; | ||
| 274 | |||
| 275 | pdata->int_config = local_pdata.int_config; | ||
| 276 | for (ii = 0; ii < DIM(pdata->orientation); ii++) | ||
| 277 | pdata->orientation[ii] = local_pdata.orientation[ii]; | ||
| 278 | pdata->level_shifter = local_pdata.level_shifter; | ||
| 279 | |||
| 280 | pdata->accel.address = local_pdata.accel.address; | ||
| 281 | for (ii = 0; ii < DIM(pdata->accel.orientation); ii++) | ||
| 282 | pdata->accel.orientation[ii] = | ||
| 283 | local_pdata.accel.orientation[ii]; | ||
| 284 | |||
| 285 | pdata->compass.address = local_pdata.compass.address; | ||
| 286 | for (ii = 0; ii < DIM(pdata->compass.orientation); ii++) | ||
| 287 | pdata->compass.orientation[ii] = | ||
| 288 | local_pdata.compass.orientation[ii]; | ||
| 289 | |||
| 290 | pdata->pressure.address = local_pdata.pressure.address; | ||
| 291 | for (ii = 0; ii < DIM(pdata->pressure.orientation); ii++) | ||
| 292 | pdata->pressure.orientation[ii] = | ||
| 293 | local_pdata.pressure.orientation[ii]; | ||
| 294 | |||
| 295 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 296 | |||
| 297 | return ML_SUCCESS; | ||
| 298 | } | ||
| 299 | |||
| 300 | static int | ||
| 301 | mpu_ioctl_set_mpu_config(struct i2c_client *client, unsigned long arg) | ||
| 302 | { | ||
| 303 | int ii; | ||
| 304 | int result = ML_SUCCESS; | ||
| 305 | struct mpu_private_data *mpu = | ||
| 306 | (struct mpu_private_data *) i2c_get_clientdata(client); | ||
| 307 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 308 | struct mldl_cfg *temp_mldl_cfg; | ||
| 309 | |||
| 310 | dev_dbg(&this_client->adapter->dev, "%s\n", __func__); | ||
| 311 | |||
| 312 | temp_mldl_cfg = kzalloc(sizeof(struct mldl_cfg), GFP_KERNEL); | ||
| 313 | if (NULL == temp_mldl_cfg) | ||
| 314 | return -ENOMEM; | ||
| 315 | |||
| 316 | /* | ||
| 317 | * User space is not allowed to modify accel compass pressure or | ||
| 318 | * pdata structs, as well as silicon_revision product_id or trim | ||
| 319 | */ | ||
| 320 | if (copy_from_user(temp_mldl_cfg, (struct mldl_cfg __user *) arg, | ||
| 321 | offsetof(struct mldl_cfg, silicon_revision))) { | ||
| 322 | result = -EFAULT; | ||
| 323 | goto out; | ||
| 324 | } | ||
| 325 | |||
| 326 | if (mldl_cfg->gyro_is_suspended) { | ||
| 327 | if (mldl_cfg->addr != temp_mldl_cfg->addr) | ||
| 328 | mldl_cfg->gyro_needs_reset = TRUE; | ||
| 329 | |||
| 330 | if (mldl_cfg->int_config != temp_mldl_cfg->int_config) | ||
| 331 | mldl_cfg->gyro_needs_reset = TRUE; | ||
| 332 | |||
| 333 | if (mldl_cfg->ext_sync != temp_mldl_cfg->ext_sync) | ||
| 334 | mldl_cfg->gyro_needs_reset = TRUE; | ||
| 335 | |||
| 336 | if (mldl_cfg->full_scale != temp_mldl_cfg->full_scale) | ||
| 337 | mldl_cfg->gyro_needs_reset = TRUE; | ||
| 338 | |||
| 339 | if (mldl_cfg->lpf != temp_mldl_cfg->lpf) | ||
| 340 | mldl_cfg->gyro_needs_reset = TRUE; | ||
| 341 | |||
| 342 | if (mldl_cfg->clk_src != temp_mldl_cfg->clk_src) | ||
| 343 | mldl_cfg->gyro_needs_reset = TRUE; | ||
| 344 | |||
| 345 | if (mldl_cfg->divider != temp_mldl_cfg->divider) | ||
| 346 | mldl_cfg->gyro_needs_reset = TRUE; | ||
| 347 | |||
| 348 | if (mldl_cfg->dmp_enable != temp_mldl_cfg->dmp_enable) | ||
| 349 | mldl_cfg->gyro_needs_reset = TRUE; | ||
| 350 | |||
| 351 | if (mldl_cfg->fifo_enable != temp_mldl_cfg->fifo_enable) | ||
| 352 | mldl_cfg->gyro_needs_reset = TRUE; | ||
| 353 | |||
| 354 | if (mldl_cfg->dmp_cfg1 != temp_mldl_cfg->dmp_cfg1) | ||
| 355 | mldl_cfg->gyro_needs_reset = TRUE; | ||
| 356 | |||
| 357 | if (mldl_cfg->dmp_cfg2 != temp_mldl_cfg->dmp_cfg2) | ||
| 358 | mldl_cfg->gyro_needs_reset = TRUE; | ||
| 359 | |||
| 360 | if (mldl_cfg->gyro_power != temp_mldl_cfg->gyro_power) | ||
| 361 | mldl_cfg->gyro_needs_reset = TRUE; | ||
| 362 | |||
| 363 | for (ii = 0; ii < MPU_NUM_AXES; ii++) | ||
| 364 | if (mldl_cfg->offset_tc[ii] != | ||
| 365 | temp_mldl_cfg->offset_tc[ii]) | ||
| 366 | mldl_cfg->gyro_needs_reset = TRUE; | ||
| 367 | |||
| 368 | for (ii = 0; ii < MPU_NUM_AXES; ii++) | ||
| 369 | if (mldl_cfg->offset[ii] != temp_mldl_cfg->offset[ii]) | ||
| 370 | mldl_cfg->gyro_needs_reset = TRUE; | ||
| 371 | |||
| 372 | if (memcmp(mldl_cfg->ram, temp_mldl_cfg->ram, | ||
| 373 | MPU_MEM_NUM_RAM_BANKS * MPU_MEM_BANK_SIZE * | ||
| 374 | sizeof(unsigned char))) | ||
| 375 | mldl_cfg->gyro_needs_reset = TRUE; | ||
| 376 | } | ||
| 377 | |||
| 378 | memcpy(mldl_cfg, temp_mldl_cfg, | ||
| 379 | offsetof(struct mldl_cfg, silicon_revision)); | ||
| 380 | |||
| 381 | out: | ||
| 382 | kfree(temp_mldl_cfg); | ||
| 383 | return result; | ||
| 384 | } | ||
| 385 | |||
| 386 | static int | ||
| 387 | mpu_ioctl_get_mpu_config(struct i2c_client *client, unsigned long arg) | ||
| 388 | { | ||
| 389 | /* Have to be careful as there are 3 pointers in the mldl_cfg | ||
| 390 | * structure */ | ||
| 391 | struct mpu_private_data *mpu = | ||
| 392 | (struct mpu_private_data *) i2c_get_clientdata(client); | ||
| 393 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 394 | struct mldl_cfg *local_mldl_cfg; | ||
| 395 | int retval = 0; | ||
| 396 | |||
| 397 | local_mldl_cfg = kzalloc(sizeof(struct mldl_cfg), GFP_KERNEL); | ||
| 398 | if (NULL == local_mldl_cfg) | ||
| 399 | return -ENOMEM; | ||
| 400 | |||
| 401 | retval = | ||
| 402 | copy_from_user(local_mldl_cfg, (struct mldl_cfg __user *) arg, | ||
| 403 | sizeof(struct mldl_cfg)); | ||
| 404 | if (retval) { | ||
| 405 | dev_err(&this_client->adapter->dev, | ||
| 406 | "%s|%s:%d: EFAULT on arg\n", | ||
| 407 | __FILE__, __func__, __LINE__); | ||
| 408 | retval = -EFAULT; | ||
| 409 | goto out; | ||
| 410 | } | ||
| 411 | |||
| 412 | /* Fill in the accel, compass, pressure and pdata pointers */ | ||
| 413 | if (mldl_cfg->accel) { | ||
| 414 | retval = copy_to_user((void __user *)local_mldl_cfg->accel, | ||
| 415 | mldl_cfg->accel, | ||
| 416 | sizeof(*mldl_cfg->accel)); | ||
| 417 | if (retval) { | ||
| 418 | dev_err(&this_client->adapter->dev, | ||
| 419 | "%s|%s:%d: EFAULT on accel\n", | ||
| 420 | __FILE__, __func__, __LINE__); | ||
| 421 | retval = -EFAULT; | ||
| 422 | goto out; | ||
| 423 | } | ||
| 424 | } | ||
| 425 | |||
| 426 | if (mldl_cfg->compass) { | ||
| 427 | retval = copy_to_user((void __user *)local_mldl_cfg->compass, | ||
| 428 | mldl_cfg->compass, | ||
| 429 | sizeof(*mldl_cfg->compass)); | ||
| 430 | if (retval) { | ||
| 431 | dev_err(&this_client->adapter->dev, | ||
| 432 | "%s|%s:%d: EFAULT on compass\n", | ||
| 433 | __FILE__, __func__, __LINE__); | ||
| 434 | retval = -EFAULT; | ||
| 435 | goto out; | ||
| 436 | } | ||
| 437 | } | ||
| 438 | |||
| 439 | if (mldl_cfg->pressure) { | ||
| 440 | retval = copy_to_user((void __user *)local_mldl_cfg->pressure, | ||
| 441 | mldl_cfg->pressure, | ||
| 442 | sizeof(*mldl_cfg->pressure)); | ||
| 443 | if (retval) { | ||
| 444 | dev_err(&this_client->adapter->dev, | ||
| 445 | "%s|%s:%d: EFAULT on pressure\n", | ||
| 446 | __FILE__, __func__, __LINE__); | ||
| 447 | retval = -EFAULT; | ||
| 448 | goto out; | ||
| 449 | } | ||
| 450 | } | ||
| 451 | |||
| 452 | if (mldl_cfg->pdata) { | ||
| 453 | retval = copy_to_user((void __user *)local_mldl_cfg->pdata, | ||
| 454 | mldl_cfg->pdata, | ||
| 455 | sizeof(*mldl_cfg->pdata)); | ||
| 456 | if (retval) { | ||
| 457 | dev_err(&this_client->adapter->dev, | ||
| 458 | "%s|%s:%d: EFAULT on pdata\n", | ||
| 459 | __FILE__, __func__, __LINE__); | ||
| 460 | retval = -EFAULT; | ||
| 461 | goto out; | ||
| 462 | } | ||
| 463 | } | ||
| 464 | |||
| 465 | /* Do not modify the accel, compass, pressure and pdata pointers */ | ||
| 466 | retval = copy_to_user((struct mldl_cfg __user *) arg, | ||
| 467 | mldl_cfg, offsetof(struct mldl_cfg, accel)); | ||
| 468 | |||
| 469 | if (retval) | ||
| 470 | retval = -EFAULT; | ||
| 471 | out: | ||
| 472 | kfree(local_mldl_cfg); | ||
| 473 | return retval; | ||
| 474 | } | ||
| 475 | |||
| 476 | /** | ||
| 477 | * Pass a requested slave configuration to the slave sensor | ||
| 478 | * | ||
| 479 | * @param adapter the adaptor to use to communicate with the slave | ||
| 480 | * @param mldl_cfg the mldl configuration structuer | ||
| 481 | * @param slave pointer to the slave descriptor | ||
| 482 | * @param usr_config The configuration to pass to the slave sensor | ||
| 483 | * | ||
| 484 | * @return 0 or non-zero error code | ||
| 485 | */ | ||
| 486 | static int slave_config(void *adapter, | ||
| 487 | struct mldl_cfg *mldl_cfg, | ||
| 488 | struct ext_slave_descr *slave, | ||
| 489 | struct ext_slave_platform_data *pdata, | ||
| 490 | struct ext_slave_config __user *usr_config) | ||
| 491 | { | ||
| 492 | int retval = ML_SUCCESS; | ||
| 493 | struct ext_slave_config config; | ||
| 494 | if ((!slave) || (!slave->config)) | ||
| 495 | return retval; | ||
| 496 | |||
| 497 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
| 498 | if (retval) | ||
| 499 | return -EFAULT; | ||
| 500 | |||
| 501 | if (config.len && config.data) { | ||
| 502 | int *data; | ||
| 503 | data = kzalloc(config.len, GFP_KERNEL); | ||
| 504 | if (!data) | ||
| 505 | return ML_ERROR_MEMORY_EXAUSTED; | ||
| 506 | |||
| 507 | retval = copy_from_user(data, | ||
| 508 | (void __user *)config.data, | ||
| 509 | config.len); | ||
| 510 | if (retval) { | ||
| 511 | retval = -EFAULT; | ||
| 512 | kfree(data); | ||
| 513 | return retval; | ||
| 514 | } | ||
| 515 | config.data = data; | ||
| 516 | } | ||
| 517 | retval = slave->config(adapter, slave, pdata, &config); | ||
| 518 | kfree(config.data); | ||
| 519 | return retval; | ||
| 520 | } | ||
| 521 | |||
| 522 | /** | ||
| 523 | * Get a requested slave configuration from the slave sensor | ||
| 524 | * | ||
| 525 | * @param adapter the adaptor to use to communicate with the slave | ||
| 526 | * @param mldl_cfg the mldl configuration structuer | ||
| 527 | * @param slave pointer to the slave descriptor | ||
| 528 | * @param usr_config The configuration for the slave to fill out | ||
| 529 | * | ||
| 530 | * @return 0 or non-zero error code | ||
| 531 | */ | ||
| 532 | static int slave_get_config(void *adapter, | ||
| 533 | struct mldl_cfg *mldl_cfg, | ||
| 534 | struct ext_slave_descr *slave, | ||
| 535 | struct ext_slave_platform_data *pdata, | ||
| 536 | struct ext_slave_config __user *usr_config) | ||
| 537 | { | ||
| 538 | int retval = ML_SUCCESS; | ||
| 539 | struct ext_slave_config config; | ||
| 540 | void *user_data; | ||
| 541 | if (!(slave) || !(slave->get_config)) | ||
| 542 | return ML_SUCCESS; | ||
| 543 | |||
| 544 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
| 545 | if (retval) | ||
| 546 | return -EFAULT; | ||
| 547 | |||
| 548 | user_data = config.data; | ||
| 549 | if (config.len && config.data) { | ||
| 550 | int *data; | ||
| 551 | data = kzalloc(config.len, GFP_KERNEL); | ||
| 552 | if (!data) | ||
| 553 | return ML_ERROR_MEMORY_EXAUSTED; | ||
| 554 | |||
| 555 | retval = copy_from_user(data, | ||
| 556 | (void __user *)config.data, | ||
| 557 | config.len); | ||
| 558 | if (retval) { | ||
| 559 | retval = -EFAULT; | ||
| 560 | kfree(data); | ||
| 561 | return retval; | ||
| 562 | } | ||
| 563 | config.data = data; | ||
| 564 | } | ||
| 565 | retval = slave->get_config(adapter, slave, pdata, &config); | ||
| 566 | if (retval) { | ||
| 567 | kfree(config.data); | ||
| 568 | return retval; | ||
| 569 | } | ||
| 570 | retval = copy_to_user((unsigned char __user *) user_data, | ||
| 571 | config.data, | ||
| 572 | config.len); | ||
| 573 | kfree(config.data); | ||
| 574 | return retval; | ||
| 575 | } | ||
| 576 | |||
| 577 | static int mpu_handle_mlsl(void *sl_handle, | ||
| 578 | unsigned char addr, | ||
| 579 | unsigned int cmd, | ||
| 580 | struct mpu_read_write __user *usr_msg) | ||
| 581 | { | ||
| 582 | int retval = ML_SUCCESS; | ||
| 583 | struct mpu_read_write msg; | ||
| 584 | unsigned char *user_data; | ||
| 585 | retval = copy_from_user(&msg, usr_msg, sizeof(msg)); | ||
| 586 | if (retval) | ||
| 587 | return -EFAULT; | ||
| 588 | |||
| 589 | user_data = msg.data; | ||
| 590 | if (msg.length && msg.data) { | ||
| 591 | unsigned char *data; | ||
| 592 | data = kzalloc(msg.length, GFP_KERNEL); | ||
| 593 | if (!data) | ||
| 594 | return ML_ERROR_MEMORY_EXAUSTED; | ||
| 595 | |||
| 596 | retval = copy_from_user(data, | ||
| 597 | (void __user *)msg.data, | ||
| 598 | msg.length); | ||
| 599 | if (retval) { | ||
| 600 | retval = -EFAULT; | ||
| 601 | kfree(data); | ||
| 602 | return retval; | ||
| 603 | } | ||
| 604 | msg.data = data; | ||
| 605 | } else { | ||
| 606 | return ML_ERROR_INVALID_PARAMETER; | ||
| 607 | } | ||
| 608 | |||
| 609 | switch (cmd) { | ||
| 610 | case MPU_READ: | ||
| 611 | retval = MLSLSerialRead(sl_handle, addr, | ||
| 612 | msg.address, msg.length, msg.data); | ||
| 613 | break; | ||
| 614 | case MPU_WRITE: | ||
| 615 | retval = MLSLSerialWrite(sl_handle, addr, | ||
| 616 | msg.length, msg.data); | ||
| 617 | break; | ||
| 618 | case MPU_READ_MEM: | ||
| 619 | retval = MLSLSerialReadMem(sl_handle, addr, | ||
| 620 | msg.address, msg.length, msg.data); | ||
| 621 | break; | ||
| 622 | case MPU_WRITE_MEM: | ||
| 623 | retval = MLSLSerialWriteMem(sl_handle, addr, | ||
| 624 | msg.address, msg.length, msg.data); | ||
| 625 | break; | ||
| 626 | case MPU_READ_FIFO: | ||
| 627 | retval = MLSLSerialReadFifo(sl_handle, addr, | ||
| 628 | msg.length, msg.data); | ||
| 629 | break; | ||
| 630 | case MPU_WRITE_FIFO: | ||
| 631 | retval = MLSLSerialWriteFifo(sl_handle, addr, | ||
| 632 | msg.length, msg.data); | ||
| 633 | break; | ||
| 634 | |||
| 635 | }; | ||
| 636 | retval = copy_to_user((unsigned char __user *) user_data, | ||
| 637 | msg.data, | ||
| 638 | msg.length); | ||
| 639 | kfree(msg.data); | ||
| 640 | return retval; | ||
| 641 | } | ||
| 642 | |||
| 643 | /* ioctl - I/O control */ | ||
| 644 | static long mpu_ioctl(struct file *file, | ||
| 645 | unsigned int cmd, unsigned long arg) | ||
| 646 | { | ||
| 647 | struct i2c_client *client = | ||
| 648 | (struct i2c_client *) file->private_data; | ||
| 649 | struct mpu_private_data *mpu = | ||
| 650 | (struct mpu_private_data *) i2c_get_clientdata(client); | ||
| 651 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 652 | int retval = 0; | ||
| 653 | struct i2c_adapter *accel_adapter; | ||
| 654 | struct i2c_adapter *compass_adapter; | ||
| 655 | struct i2c_adapter *pressure_adapter; | ||
| 656 | |||
| 657 | accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num); | ||
| 658 | compass_adapter = | ||
| 659 | i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num); | ||
| 660 | pressure_adapter = | ||
| 661 | i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num); | ||
| 662 | |||
| 663 | retval = mutex_lock_interruptible(&mpu->mutex); | ||
| 664 | if (retval) { | ||
| 665 | dev_err(&this_client->adapter->dev, | ||
| 666 | "%s: mutex_lock_interruptible returned %d\n", | ||
| 667 | __func__, retval); | ||
| 668 | return retval; | ||
| 669 | } | ||
| 670 | |||
| 671 | switch (cmd) { | ||
| 672 | case MPU_SET_MPU_CONFIG: | ||
| 673 | retval = mpu_ioctl_set_mpu_config(client, arg); | ||
| 674 | break; | ||
| 675 | case MPU_SET_PLATFORM_DATA: | ||
| 676 | retval = mpu_ioctl_set_mpu_pdata(client, arg); | ||
| 677 | break; | ||
| 678 | case MPU_GET_MPU_CONFIG: | ||
| 679 | retval = mpu_ioctl_get_mpu_config(client, arg); | ||
| 680 | break; | ||
| 681 | case MPU_READ: | ||
| 682 | case MPU_WRITE: | ||
| 683 | case MPU_READ_MEM: | ||
| 684 | case MPU_WRITE_MEM: | ||
| 685 | case MPU_READ_FIFO: | ||
| 686 | case MPU_WRITE_FIFO: | ||
| 687 | retval = mpu_handle_mlsl(client->adapter, mldl_cfg->addr, cmd, | ||
| 688 | (struct mpu_read_write __user *) arg); | ||
| 689 | break; | ||
| 690 | case MPU_CONFIG_ACCEL: | ||
| 691 | retval = slave_config(accel_adapter, mldl_cfg, | ||
| 692 | mldl_cfg->accel, | ||
| 693 | &mldl_cfg->pdata->accel, | ||
| 694 | (struct ext_slave_config __user *) arg); | ||
| 695 | break; | ||
| 696 | case MPU_CONFIG_COMPASS: | ||
| 697 | retval = slave_config(compass_adapter, mldl_cfg, | ||
| 698 | mldl_cfg->compass, | ||
| 699 | &mldl_cfg->pdata->compass, | ||
| 700 | (struct ext_slave_config __user *) arg); | ||
| 701 | break; | ||
| 702 | case MPU_CONFIG_PRESSURE: | ||
| 703 | retval = slave_config(pressure_adapter, mldl_cfg, | ||
| 704 | mldl_cfg->pressure, | ||
| 705 | &mldl_cfg->pdata->pressure, | ||
| 706 | (struct ext_slave_config __user *) arg); | ||
| 707 | break; | ||
| 708 | case MPU_GET_CONFIG_ACCEL: | ||
| 709 | retval = slave_get_config(accel_adapter, mldl_cfg, | ||
| 710 | mldl_cfg->accel, | ||
| 711 | &mldl_cfg->pdata->accel, | ||
| 712 | (struct ext_slave_config __user *) arg); | ||
| 713 | break; | ||
| 714 | case MPU_GET_CONFIG_COMPASS: | ||
| 715 | retval = slave_get_config(compass_adapter, mldl_cfg, | ||
| 716 | mldl_cfg->compass, | ||
| 717 | &mldl_cfg->pdata->compass, | ||
| 718 | (struct ext_slave_config __user *) arg); | ||
| 719 | break; | ||
| 720 | case MPU_GET_CONFIG_PRESSURE: | ||
| 721 | retval = slave_get_config(pressure_adapter, mldl_cfg, | ||
| 722 | mldl_cfg->pressure, | ||
| 723 | &mldl_cfg->pdata->pressure, | ||
| 724 | (struct ext_slave_config __user *) arg); | ||
| 725 | break; | ||
| 726 | case MPU_SUSPEND: | ||
| 727 | { | ||
| 728 | unsigned long sensors; | ||
| 729 | sensors = ~(mldl_cfg->requested_sensors); | ||
| 730 | retval = mpu3050_suspend(mldl_cfg, | ||
| 731 | client->adapter, | ||
| 732 | accel_adapter, | ||
| 733 | compass_adapter, | ||
| 734 | pressure_adapter, | ||
| 735 | ((sensors & ML_THREE_AXIS_GYRO) | ||
| 736 | == ML_THREE_AXIS_GYRO), | ||
| 737 | ((sensors & ML_THREE_AXIS_ACCEL) | ||
| 738 | == ML_THREE_AXIS_ACCEL), | ||
| 739 | ((sensors & ML_THREE_AXIS_COMPASS) | ||
| 740 | == ML_THREE_AXIS_COMPASS), | ||
| 741 | ((sensors & ML_THREE_AXIS_PRESSURE) | ||
| 742 | == ML_THREE_AXIS_PRESSURE)); | ||
| 743 | } | ||
| 744 | break; | ||
| 745 | case MPU_RESUME: | ||
| 746 | { | ||
| 747 | unsigned long sensors; | ||
| 748 | sensors = mldl_cfg->requested_sensors; | ||
| 749 | retval = mpu3050_resume(mldl_cfg, | ||
| 750 | client->adapter, | ||
| 751 | accel_adapter, | ||
| 752 | compass_adapter, | ||
| 753 | pressure_adapter, | ||
| 754 | sensors & ML_THREE_AXIS_GYRO, | ||
| 755 | sensors & ML_THREE_AXIS_ACCEL, | ||
| 756 | sensors & ML_THREE_AXIS_COMPASS, | ||
| 757 | sensors & ML_THREE_AXIS_PRESSURE); | ||
| 758 | } | ||
| 759 | break; | ||
| 760 | case MPU_PM_EVENT_HANDLED: | ||
| 761 | dev_dbg(&this_client->adapter->dev, | ||
| 762 | "%s: %d\n", __func__, cmd); | ||
| 763 | complete(&mpu->completion); | ||
| 764 | break; | ||
| 765 | case MPU_READ_ACCEL: | ||
| 766 | { | ||
| 767 | unsigned char data[6]; | ||
| 768 | retval = mpu3050_read_accel(mldl_cfg, client->adapter, | ||
| 769 | data); | ||
| 770 | if ((ML_SUCCESS == retval) && | ||
| 771 | (copy_to_user((unsigned char __user *) arg, | ||
| 772 | data, sizeof(data)))) | ||
| 773 | retval = -EFAULT; | ||
| 774 | } | ||
| 775 | break; | ||
| 776 | case MPU_READ_COMPASS: | ||
| 777 | { | ||
| 778 | unsigned char data[6]; | ||
| 779 | struct i2c_adapter *compass_adapt = | ||
| 780 | i2c_get_adapter(mldl_cfg->pdata->compass. | ||
| 781 | adapt_num); | ||
| 782 | retval = mpu3050_read_compass(mldl_cfg, compass_adapt, | ||
| 783 | data); | ||
| 784 | if ((ML_SUCCESS == retval) && | ||
| 785 | (copy_to_user((unsigned char *) arg, | ||
| 786 | data, sizeof(data)))) | ||
| 787 | retval = -EFAULT; | ||
| 788 | } | ||
| 789 | break; | ||
| 790 | case MPU_READ_PRESSURE: | ||
| 791 | { | ||
| 792 | unsigned char data[3]; | ||
| 793 | struct i2c_adapter *pressure_adapt = | ||
| 794 | i2c_get_adapter(mldl_cfg->pdata->pressure. | ||
| 795 | adapt_num); | ||
| 796 | retval = | ||
| 797 | mpu3050_read_pressure(mldl_cfg, pressure_adapt, | ||
| 798 | data); | ||
| 799 | if ((ML_SUCCESS == retval) && | ||
| 800 | (copy_to_user((unsigned char __user *) arg, | ||
| 801 | data, sizeof(data)))) | ||
| 802 | retval = -EFAULT; | ||
| 803 | } | ||
| 804 | break; | ||
| 805 | default: | ||
| 806 | dev_err(&this_client->adapter->dev, | ||
| 807 | "%s: Unknown cmd %x, arg %lu\n", __func__, cmd, | ||
| 808 | arg); | ||
| 809 | retval = -EINVAL; | ||
| 810 | } | ||
| 811 | |||
| 812 | mutex_unlock(&mpu->mutex); | ||
| 813 | return retval; | ||
| 814 | } | ||
| 815 | |||
| 816 | #ifdef CONFIG_HAS_EARLYSUSPEND | ||
| 817 | void mpu3050_early_suspend(struct early_suspend *h) | ||
| 818 | { | ||
| 819 | struct mpu_private_data *mpu = container_of(h, | ||
| 820 | struct | ||
| 821 | mpu_private_data, | ||
| 822 | early_suspend); | ||
| 823 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 824 | struct i2c_adapter *accel_adapter; | ||
| 825 | struct i2c_adapter *compass_adapter; | ||
| 826 | struct i2c_adapter *pressure_adapter; | ||
| 827 | |||
| 828 | accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num); | ||
| 829 | compass_adapter = | ||
| 830 | i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num); | ||
| 831 | pressure_adapter = | ||
| 832 | i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num); | ||
| 833 | |||
| 834 | dev_dbg(&this_client->adapter->dev, "%s: %d, %d\n", __func__, | ||
| 835 | h->level, mpu->mldl_cfg.gyro_is_suspended); | ||
| 836 | if (MPU3050_EARLY_SUSPEND_IN_DRIVER) { | ||
| 837 | mutex_lock(&mpu->mutex); | ||
| 838 | (void) mpu3050_suspend(mldl_cfg, this_client->adapter, | ||
| 839 | accel_adapter, compass_adapter, | ||
| 840 | pressure_adapter, TRUE, TRUE, TRUE, TRUE); | ||
| 841 | mutex_unlock(&mpu->mutex); | ||
| 842 | } | ||
| 843 | } | ||
| 844 | |||
| 845 | void mpu3050_early_resume(struct early_suspend *h) | ||
| 846 | { | ||
| 847 | struct mpu_private_data *mpu = container_of(h, | ||
| 848 | struct | ||
| 849 | mpu_private_data, | ||
| 850 | early_suspend); | ||
| 851 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 852 | struct i2c_adapter *accel_adapter; | ||
| 853 | struct i2c_adapter *compass_adapter; | ||
| 854 | struct i2c_adapter *pressure_adapter; | ||
| 855 | |||
| 856 | accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num); | ||
| 857 | compass_adapter = | ||
| 858 | i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num); | ||
| 859 | pressure_adapter = | ||
| 860 | i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num); | ||
| 861 | |||
| 862 | if (MPU3050_EARLY_SUSPEND_IN_DRIVER) { | ||
| 863 | if (mpu->pid) { | ||
| 864 | unsigned long sensors = mldl_cfg->requested_sensors; | ||
| 865 | mutex_lock(&mpu->mutex); | ||
| 866 | (void) mpu3050_resume(mldl_cfg, | ||
| 867 | this_client->adapter, | ||
| 868 | accel_adapter, | ||
| 869 | compass_adapter, | ||
| 870 | pressure_adapter, | ||
| 871 | sensors & ML_THREE_AXIS_GYRO, | ||
| 872 | sensors & ML_THREE_AXIS_ACCEL, | ||
| 873 | sensors & ML_THREE_AXIS_COMPASS, | ||
| 874 | sensors & ML_THREE_AXIS_PRESSURE); | ||
| 875 | mutex_unlock(&mpu->mutex); | ||
| 876 | dev_dbg(&this_client->adapter->dev, | ||
| 877 | "%s for pid %d\n", __func__, mpu->pid); | ||
| 878 | } | ||
| 879 | } | ||
| 880 | dev_dbg(&this_client->adapter->dev, "%s: %d\n", __func__, h->level); | ||
| 881 | } | ||
| 882 | #endif | ||
| 883 | |||
| 884 | void mpu_shutdown(struct i2c_client *client) | ||
| 885 | { | ||
| 886 | struct mpu_private_data *mpu = | ||
| 887 | (struct mpu_private_data *) i2c_get_clientdata(client); | ||
| 888 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 889 | struct i2c_adapter *accel_adapter; | ||
| 890 | struct i2c_adapter *compass_adapter; | ||
| 891 | struct i2c_adapter *pressure_adapter; | ||
| 892 | |||
| 893 | accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num); | ||
| 894 | compass_adapter = | ||
| 895 | i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num); | ||
| 896 | pressure_adapter = | ||
| 897 | i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num); | ||
| 898 | |||
| 899 | mutex_lock(&mpu->mutex); | ||
| 900 | (void) mpu3050_suspend(mldl_cfg, this_client->adapter, | ||
| 901 | accel_adapter, compass_adapter, pressure_adapter, | ||
| 902 | TRUE, TRUE, TRUE, TRUE); | ||
| 903 | mutex_unlock(&mpu->mutex); | ||
| 904 | dev_dbg(&this_client->adapter->dev, "%s\n", __func__); | ||
| 905 | } | ||
| 906 | |||
| 907 | int mpu_suspend(struct i2c_client *client, pm_message_t mesg) | ||
| 908 | { | ||
| 909 | struct mpu_private_data *mpu = | ||
| 910 | (struct mpu_private_data *) i2c_get_clientdata(client); | ||
| 911 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 912 | struct i2c_adapter *accel_adapter; | ||
| 913 | struct i2c_adapter *compass_adapter; | ||
| 914 | struct i2c_adapter *pressure_adapter; | ||
| 915 | |||
| 916 | accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num); | ||
| 917 | compass_adapter = | ||
| 918 | i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num); | ||
| 919 | pressure_adapter = | ||
| 920 | i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num); | ||
| 921 | |||
| 922 | mutex_lock(&mpu->mutex); | ||
| 923 | if (!mldl_cfg->ignore_system_suspend) { | ||
| 924 | dev_dbg(&this_client->adapter->dev, | ||
| 925 | "%s: suspending on event %d\n", __func__, | ||
| 926 | mesg.event); | ||
| 927 | (void) mpu3050_suspend(mldl_cfg, this_client->adapter, | ||
| 928 | accel_adapter, compass_adapter, | ||
| 929 | pressure_adapter, | ||
| 930 | TRUE, TRUE, TRUE, TRUE); | ||
| 931 | } else { | ||
| 932 | dev_dbg(&this_client->adapter->dev, | ||
| 933 | "%s: Already suspended %d\n", __func__, | ||
| 934 | mesg.event); | ||
| 935 | } | ||
| 936 | mutex_unlock(&mpu->mutex); | ||
| 937 | return 0; | ||
| 938 | } | ||
| 939 | |||
| 940 | int mpu_resume(struct i2c_client *client) | ||
| 941 | { | ||
| 942 | struct mpu_private_data *mpu = | ||
| 943 | (struct mpu_private_data *) i2c_get_clientdata(client); | ||
| 944 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 945 | struct i2c_adapter *accel_adapter; | ||
| 946 | struct i2c_adapter *compass_adapter; | ||
| 947 | struct i2c_adapter *pressure_adapter; | ||
| 948 | |||
| 949 | accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num); | ||
| 950 | compass_adapter = | ||
| 951 | i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num); | ||
| 952 | pressure_adapter = | ||
| 953 | i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num); | ||
| 954 | |||
| 955 | mutex_lock(&mpu->mutex); | ||
| 956 | if (mpu->pid && !mldl_cfg->ignore_system_suspend) { | ||
| 957 | unsigned long sensors = mldl_cfg->requested_sensors; | ||
| 958 | (void) mpu3050_resume(mldl_cfg, this_client->adapter, | ||
| 959 | accel_adapter, | ||
| 960 | compass_adapter, | ||
| 961 | pressure_adapter, | ||
| 962 | sensors & ML_THREE_AXIS_GYRO, | ||
| 963 | sensors & ML_THREE_AXIS_ACCEL, | ||
| 964 | sensors & ML_THREE_AXIS_COMPASS, | ||
| 965 | sensors & ML_THREE_AXIS_PRESSURE); | ||
| 966 | dev_dbg(&this_client->adapter->dev, | ||
| 967 | "%s for pid %d\n", __func__, mpu->pid); | ||
| 968 | } | ||
| 969 | mutex_unlock(&mpu->mutex); | ||
| 970 | return 0; | ||
| 971 | } | ||
| 972 | |||
| 973 | /* define which file operations are supported */ | ||
| 974 | static const struct file_operations mpu_fops = { | ||
| 975 | .owner = THIS_MODULE, | ||
| 976 | .read = mpu_read, | ||
| 977 | .poll = mpu_poll, | ||
| 978 | |||
| 979 | #if HAVE_COMPAT_IOCTL | ||
| 980 | .compat_ioctl = mpu_ioctl, | ||
| 981 | #endif | ||
| 982 | #if HAVE_UNLOCKED_IOCTL | ||
| 983 | .unlocked_ioctl = mpu_ioctl, | ||
| 984 | #endif | ||
| 985 | .open = mpu_open, | ||
| 986 | .release = mpu_release, | ||
| 987 | }; | ||
| 988 | |||
| 989 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 990 | |||
| 991 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | ||
| 992 | I2C_CLIENT_INSMOD; | ||
| 993 | #endif | ||
| 994 | |||
| 995 | static struct miscdevice i2c_mpu_device = { | ||
| 996 | .minor = MISC_DYNAMIC_MINOR, | ||
| 997 | .name = "mpu", /* Same for both 3050 and 6000 */ | ||
| 998 | .fops = &mpu_fops, | ||
| 999 | }; | ||
| 1000 | |||
| 1001 | |||
| 1002 | int mpu3050_probe(struct i2c_client *client, | ||
| 1003 | const struct i2c_device_id *devid) | ||
| 1004 | { | ||
| 1005 | struct mpu3050_platform_data *pdata; | ||
| 1006 | struct mpu_private_data *mpu; | ||
| 1007 | struct mldl_cfg *mldl_cfg; | ||
| 1008 | int res = 0; | ||
| 1009 | struct i2c_adapter *accel_adapter = NULL; | ||
| 1010 | struct i2c_adapter *compass_adapter = NULL; | ||
| 1011 | struct i2c_adapter *pressure_adapter = NULL; | ||
| 1012 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 1013 | |||
| 1014 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 1015 | res = -ENODEV; | ||
| 1016 | goto out_check_functionality_failed; | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | mpu = kzalloc(sizeof(struct mpu_private_data), GFP_KERNEL); | ||
| 1020 | if (!mpu) { | ||
| 1021 | res = -ENOMEM; | ||
| 1022 | goto out_alloc_data_failed; | ||
| 1023 | } | ||
| 1024 | |||
| 1025 | i2c_set_clientdata(client, mpu); | ||
| 1026 | this_client = client; | ||
| 1027 | mldl_cfg = &mpu->mldl_cfg; | ||
| 1028 | |||
| 1029 | init_waitqueue_head(&mpu->mpu_event_wait); | ||
| 1030 | |||
| 1031 | mutex_init(&mpu->mutex); | ||
| 1032 | init_completion(&mpu->completion); | ||
| 1033 | |||
| 1034 | mpu->response_timeout = 60; /* Seconds */ | ||
| 1035 | mpu->timeout.function = mpu_pm_timeout; | ||
| 1036 | mpu->timeout.data = (u_long) mpu; | ||
| 1037 | init_timer(&mpu->timeout); | ||
| 1038 | |||
| 1039 | /* FIXME: | ||
| 1040 | * Do not register the pm_notifier as it causes | ||
| 1041 | * issues with resume sequence as there is no response | ||
| 1042 | * from user-space for power notifications for approx | ||
| 1043 | * 60 sec. Refer NV bug 858630 for more details. | ||
| 1044 | */ | ||
| 1045 | #if 0 | ||
| 1046 | mpu->nb.notifier_call = mpu_pm_notifier_callback; | ||
| 1047 | mpu->nb.priority = 0; | ||
| 1048 | register_pm_notifier(&mpu->nb); | ||
| 1049 | #endif | ||
| 1050 | |||
| 1051 | pdata = (struct mpu3050_platform_data *) client->dev.platform_data; | ||
| 1052 | if (!pdata) { | ||
| 1053 | dev_warn(&this_client->adapter->dev, | ||
| 1054 | "Missing platform data for mpu3050\n"); | ||
| 1055 | } else { | ||
| 1056 | mldl_cfg->pdata = pdata; | ||
| 1057 | |||
| 1058 | #if defined(CONFIG_MPU_SENSORS_MPU3050_MODULE) || \ | ||
| 1059 | defined(CONFIG_MPU_SENSORS_MPU6000_MODULE) | ||
| 1060 | pdata->accel.get_slave_descr = get_accel_slave_descr; | ||
| 1061 | pdata->compass.get_slave_descr = get_compass_slave_descr; | ||
| 1062 | pdata->pressure.get_slave_descr = get_pressure_slave_descr; | ||
| 1063 | #endif | ||
| 1064 | |||
| 1065 | if (pdata->accel.get_slave_descr) { | ||
| 1066 | mldl_cfg->accel = | ||
| 1067 | pdata->accel.get_slave_descr(); | ||
| 1068 | dev_info(&this_client->adapter->dev, | ||
| 1069 | "%s: +%s\n", MPU_NAME, | ||
| 1070 | mldl_cfg->accel->name); | ||
| 1071 | accel_adapter = | ||
| 1072 | i2c_get_adapter(pdata->accel.adapt_num); | ||
| 1073 | if (pdata->accel.irq > 0) { | ||
| 1074 | dev_info(&this_client->adapter->dev, | ||
| 1075 | "Installing Accel irq using %d\n", | ||
| 1076 | pdata->accel.irq); | ||
| 1077 | res = slaveirq_init(accel_adapter, | ||
| 1078 | &pdata->accel, | ||
| 1079 | "accelirq"); | ||
| 1080 | if (res) | ||
| 1081 | goto out_accelirq_failed; | ||
| 1082 | } else { | ||
| 1083 | dev_warn(&this_client->adapter->dev, | ||
| 1084 | "WARNING: Accel irq not assigned\n"); | ||
| 1085 | } | ||
| 1086 | } else { | ||
| 1087 | dev_warn(&this_client->adapter->dev, | ||
| 1088 | "%s: No Accel Present\n", MPU_NAME); | ||
| 1089 | } | ||
| 1090 | |||
| 1091 | if (pdata->compass.get_slave_descr) { | ||
| 1092 | mldl_cfg->compass = | ||
| 1093 | pdata->compass.get_slave_descr(); | ||
| 1094 | dev_info(&this_client->adapter->dev, | ||
| 1095 | "%s: +%s\n", MPU_NAME, | ||
| 1096 | mldl_cfg->compass->name); | ||
| 1097 | compass_adapter = | ||
| 1098 | i2c_get_adapter(pdata->compass.adapt_num); | ||
| 1099 | if (pdata->compass.irq > 0) { | ||
| 1100 | dev_info(&this_client->adapter->dev, | ||
| 1101 | "Installing Compass irq using %d\n", | ||
| 1102 | pdata->compass.irq); | ||
| 1103 | res = slaveirq_init(compass_adapter, | ||
| 1104 | &pdata->compass, | ||
| 1105 | "compassirq"); | ||
| 1106 | if (res) | ||
| 1107 | goto out_compassirq_failed; | ||
| 1108 | } else { | ||
| 1109 | dev_warn(&this_client->adapter->dev, | ||
| 1110 | "WARNING: Compass irq not assigned\n"); | ||
| 1111 | } | ||
| 1112 | } else { | ||
| 1113 | dev_warn(&this_client->adapter->dev, | ||
| 1114 | "%s: No Compass Present\n", MPU_NAME); | ||
| 1115 | } | ||
| 1116 | |||
| 1117 | if (pdata->pressure.get_slave_descr) { | ||
| 1118 | mldl_cfg->pressure = | ||
| 1119 | pdata->pressure.get_slave_descr(); | ||
| 1120 | dev_info(&this_client->adapter->dev, | ||
| 1121 | "%s: +%s\n", MPU_NAME, | ||
| 1122 | mldl_cfg->pressure->name); | ||
| 1123 | pressure_adapter = | ||
| 1124 | i2c_get_adapter(pdata->pressure.adapt_num); | ||
| 1125 | |||
| 1126 | if (pdata->pressure.irq > 0) { | ||
| 1127 | dev_info(&this_client->adapter->dev, | ||
| 1128 | "Installing Pressure irq using %d\n", | ||
| 1129 | pdata->pressure.irq); | ||
| 1130 | res = slaveirq_init(pressure_adapter, | ||
| 1131 | &pdata->pressure, | ||
| 1132 | "pressureirq"); | ||
| 1133 | if (res) | ||
| 1134 | goto out_pressureirq_failed; | ||
| 1135 | } else { | ||
| 1136 | dev_warn(&this_client->adapter->dev, | ||
| 1137 | "WARNING: Pressure irq not assigned\n"); | ||
| 1138 | } | ||
| 1139 | } else { | ||
| 1140 | dev_warn(&this_client->adapter->dev, | ||
| 1141 | "%s: No Pressure Present\n", MPU_NAME); | ||
| 1142 | } | ||
| 1143 | } | ||
| 1144 | |||
| 1145 | mldl_cfg->addr = client->addr; | ||
| 1146 | res = mpu3050_open(&mpu->mldl_cfg, client->adapter, | ||
| 1147 | accel_adapter, compass_adapter, pressure_adapter); | ||
| 1148 | |||
| 1149 | if (res) { | ||
| 1150 | dev_err(&this_client->adapter->dev, | ||
| 1151 | "Unable to open %s %d\n", MPU_NAME, res); | ||
| 1152 | res = -ENODEV; | ||
| 1153 | goto out_whoami_failed; | ||
| 1154 | } | ||
| 1155 | |||
| 1156 | res = misc_register(&i2c_mpu_device); | ||
| 1157 | if (res < 0) { | ||
| 1158 | dev_err(&this_client->adapter->dev, | ||
| 1159 | "ERROR: misc_register returned %d\n", res); | ||
| 1160 | goto out_misc_register_failed; | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | if (this_client->irq > 0) { | ||
| 1164 | dev_info(&this_client->adapter->dev, | ||
| 1165 | "Installing irq using %d\n", this_client->irq); | ||
| 1166 | res = mpuirq_init(this_client); | ||
| 1167 | if (res) | ||
| 1168 | goto out_mpuirq_failed; | ||
| 1169 | } else { | ||
| 1170 | dev_warn(&this_client->adapter->dev, | ||
| 1171 | "Missing %s IRQ\n", MPU_NAME); | ||
| 1172 | } | ||
| 1173 | |||
| 1174 | |||
| 1175 | #ifdef CONFIG_HAS_EARLYSUSPEND | ||
| 1176 | mpu->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; | ||
| 1177 | mpu->early_suspend.suspend = mpu3050_early_suspend; | ||
| 1178 | mpu->early_suspend.resume = mpu3050_early_resume; | ||
| 1179 | register_early_suspend(&mpu->early_suspend); | ||
| 1180 | #endif | ||
| 1181 | return res; | ||
| 1182 | |||
| 1183 | out_mpuirq_failed: | ||
| 1184 | misc_deregister(&i2c_mpu_device); | ||
| 1185 | out_misc_register_failed: | ||
| 1186 | mpu3050_close(&mpu->mldl_cfg, client->adapter, | ||
| 1187 | accel_adapter, compass_adapter, pressure_adapter); | ||
| 1188 | out_whoami_failed: | ||
| 1189 | if (pdata && | ||
| 1190 | pdata->pressure.get_slave_descr && | ||
| 1191 | pdata->pressure.irq) | ||
| 1192 | slaveirq_exit(&pdata->pressure); | ||
| 1193 | out_pressureirq_failed: | ||
| 1194 | if (pdata && | ||
| 1195 | pdata->compass.get_slave_descr && | ||
| 1196 | pdata->compass.irq) | ||
| 1197 | slaveirq_exit(&pdata->compass); | ||
| 1198 | out_compassirq_failed: | ||
| 1199 | if (pdata && | ||
| 1200 | pdata->accel.get_slave_descr && | ||
| 1201 | pdata->accel.irq) | ||
| 1202 | slaveirq_exit(&pdata->accel); | ||
| 1203 | out_accelirq_failed: | ||
| 1204 | kfree(mpu); | ||
| 1205 | out_alloc_data_failed: | ||
| 1206 | out_check_functionality_failed: | ||
| 1207 | dev_err(&this_client->adapter->dev, "%s failed %d\n", __func__, | ||
| 1208 | res); | ||
| 1209 | return res; | ||
| 1210 | |||
| 1211 | } | ||
| 1212 | |||
| 1213 | static int mpu3050_remove(struct i2c_client *client) | ||
| 1214 | { | ||
| 1215 | struct mpu_private_data *mpu = i2c_get_clientdata(client); | ||
| 1216 | struct i2c_adapter *accel_adapter; | ||
| 1217 | struct i2c_adapter *compass_adapter; | ||
| 1218 | struct i2c_adapter *pressure_adapter; | ||
| 1219 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 1220 | struct mpu3050_platform_data *pdata = mldl_cfg->pdata; | ||
| 1221 | |||
| 1222 | accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num); | ||
| 1223 | compass_adapter = | ||
| 1224 | i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num); | ||
| 1225 | pressure_adapter = | ||
| 1226 | i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num); | ||
| 1227 | |||
| 1228 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 1229 | |||
| 1230 | #ifdef CONFIG_HAS_EARLYSUSPEND | ||
| 1231 | unregister_early_suspend(&mpu->early_suspend); | ||
| 1232 | #endif | ||
| 1233 | mpu3050_close(mldl_cfg, client->adapter, | ||
| 1234 | accel_adapter, compass_adapter, pressure_adapter); | ||
| 1235 | |||
| 1236 | if (client->irq) | ||
| 1237 | mpuirq_exit(); | ||
| 1238 | |||
| 1239 | if (pdata && | ||
| 1240 | pdata->pressure.get_slave_descr && | ||
| 1241 | pdata->pressure.irq) | ||
| 1242 | slaveirq_exit(&pdata->pressure); | ||
| 1243 | |||
| 1244 | if (pdata && | ||
| 1245 | pdata->compass.get_slave_descr && | ||
| 1246 | pdata->compass.irq) | ||
| 1247 | slaveirq_exit(&pdata->compass); | ||
| 1248 | |||
| 1249 | if (pdata && | ||
| 1250 | pdata->accel.get_slave_descr && | ||
| 1251 | pdata->accel.irq) | ||
| 1252 | slaveirq_exit(&pdata->accel); | ||
| 1253 | |||
| 1254 | misc_deregister(&i2c_mpu_device); | ||
| 1255 | kfree(mpu); | ||
| 1256 | |||
| 1257 | return 0; | ||
| 1258 | } | ||
| 1259 | |||
| 1260 | static const struct i2c_device_id mpu3050_id[] = { | ||
| 1261 | {MPU_NAME, 0}, | ||
| 1262 | {} | ||
| 1263 | }; | ||
| 1264 | |||
| 1265 | MODULE_DEVICE_TABLE(i2c, mpu3050_id); | ||
| 1266 | |||
| 1267 | static struct i2c_driver mpu3050_driver = { | ||
| 1268 | .class = I2C_CLASS_HWMON, | ||
| 1269 | .probe = mpu3050_probe, | ||
| 1270 | .remove = mpu3050_remove, | ||
| 1271 | .id_table = mpu3050_id, | ||
| 1272 | .driver = { | ||
| 1273 | .owner = THIS_MODULE, | ||
| 1274 | .name = MPU_NAME, | ||
| 1275 | }, | ||
| 1276 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | ||
| 1277 | .address_data = &addr_data, | ||
| 1278 | #else | ||
| 1279 | .address_list = normal_i2c, | ||
| 1280 | #endif | ||
| 1281 | |||
| 1282 | .shutdown = mpu_shutdown, /* optional */ | ||
| 1283 | .suspend = mpu_suspend, /* optional */ | ||
| 1284 | .resume = mpu_resume, /* optional */ | ||
| 1285 | |||
| 1286 | }; | ||
| 1287 | |||
| 1288 | static int __init mpu_init(void) | ||
| 1289 | { | ||
| 1290 | int res = i2c_add_driver(&mpu3050_driver); | ||
| 1291 | pr_debug("%s\n", __func__); | ||
| 1292 | if (res) | ||
| 1293 | pr_err("%s failed\n", | ||
| 1294 | __func__); | ||
| 1295 | return res; | ||
| 1296 | } | ||
| 1297 | |||
| 1298 | static void __exit mpu_exit(void) | ||
| 1299 | { | ||
| 1300 | pr_debug("%s\n", __func__); | ||
| 1301 | i2c_del_driver(&mpu3050_driver); | ||
| 1302 | } | ||
| 1303 | |||
| 1304 | module_init(mpu_init); | ||
| 1305 | module_exit(mpu_exit); | ||
| 1306 | |||
| 1307 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 1308 | MODULE_DESCRIPTION("User space character device interface for MPU3050"); | ||
| 1309 | MODULE_LICENSE("GPL"); | ||
| 1310 | MODULE_ALIAS(MPU_NAME); | ||
diff --git a/drivers/misc/mpu3050/mpu-i2c.c b/drivers/misc/mpu3050/mpu-i2c.c new file mode 100644 index 00000000000..b1298d313ab --- /dev/null +++ b/drivers/misc/mpu3050/mpu-i2c.c | |||
| @@ -0,0 +1,196 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2010 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @defgroup | ||
| 22 | * @brief | ||
| 23 | * | ||
| 24 | * @{ | ||
| 25 | * @file mpu-i2c.c | ||
| 26 | * @brief | ||
| 27 | * | ||
| 28 | */ | ||
| 29 | |||
| 30 | #include <linux/i2c.h> | ||
| 31 | #include "mpu.h" | ||
| 32 | |||
| 33 | int sensor_i2c_write(struct i2c_adapter *i2c_adap, | ||
| 34 | unsigned char address, | ||
| 35 | unsigned int len, unsigned char const *data) | ||
| 36 | { | ||
| 37 | struct i2c_msg msgs[1]; | ||
| 38 | int res; | ||
| 39 | |||
| 40 | if (NULL == data || NULL == i2c_adap) | ||
| 41 | return -EINVAL; | ||
| 42 | |||
| 43 | msgs[0].addr = address; | ||
| 44 | msgs[0].flags = 0; /* write */ | ||
| 45 | msgs[0].buf = (unsigned char *) data; | ||
| 46 | msgs[0].len = len; | ||
| 47 | |||
| 48 | res = i2c_transfer(i2c_adap, msgs, 1); | ||
| 49 | if (res < 1) | ||
| 50 | return res; | ||
| 51 | else | ||
| 52 | return 0; | ||
| 53 | } | ||
| 54 | |||
| 55 | int sensor_i2c_write_register(struct i2c_adapter *i2c_adap, | ||
| 56 | unsigned char address, | ||
| 57 | unsigned char reg, unsigned char value) | ||
| 58 | { | ||
| 59 | unsigned char data[2]; | ||
| 60 | |||
| 61 | data[0] = reg; | ||
| 62 | data[1] = value; | ||
| 63 | return sensor_i2c_write(i2c_adap, address, 2, data); | ||
| 64 | } | ||
| 65 | |||
| 66 | int sensor_i2c_read(struct i2c_adapter *i2c_adap, | ||
| 67 | unsigned char address, | ||
| 68 | unsigned char reg, | ||
| 69 | unsigned int len, unsigned char *data) | ||
| 70 | { | ||
| 71 | struct i2c_msg msgs[2]; | ||
| 72 | int res; | ||
| 73 | |||
| 74 | if (NULL == data || NULL == i2c_adap) | ||
| 75 | return -EINVAL; | ||
| 76 | |||
| 77 | msgs[0].addr = address; | ||
| 78 | msgs[0].flags = 0; /* write */ | ||
| 79 | msgs[0].buf = ® | ||
| 80 | msgs[0].len = 1; | ||
| 81 | |||
| 82 | msgs[1].addr = address; | ||
| 83 | msgs[1].flags = I2C_M_RD; | ||
| 84 | msgs[1].buf = data; | ||
| 85 | msgs[1].len = len; | ||
| 86 | |||
| 87 | res = i2c_transfer(i2c_adap, msgs, 2); | ||
| 88 | if (res < 2) | ||
| 89 | return res; | ||
| 90 | else | ||
| 91 | return 0; | ||
| 92 | } | ||
| 93 | |||
| 94 | int mpu_memory_read(struct i2c_adapter *i2c_adap, | ||
| 95 | unsigned char mpu_addr, | ||
| 96 | unsigned short mem_addr, | ||
| 97 | unsigned int len, unsigned char *data) | ||
| 98 | { | ||
| 99 | unsigned char bank[2]; | ||
| 100 | unsigned char addr[2]; | ||
| 101 | unsigned char buf; | ||
| 102 | |||
| 103 | struct i2c_msg msgs[4]; | ||
| 104 | int ret; | ||
| 105 | |||
| 106 | if (NULL == data || NULL == i2c_adap) | ||
| 107 | return -EINVAL; | ||
| 108 | |||
| 109 | bank[0] = MPUREG_BANK_SEL; | ||
| 110 | bank[1] = mem_addr >> 8; | ||
| 111 | |||
| 112 | addr[0] = MPUREG_MEM_START_ADDR; | ||
| 113 | addr[1] = mem_addr & 0xFF; | ||
| 114 | |||
| 115 | buf = MPUREG_MEM_R_W; | ||
| 116 | |||
| 117 | /* Write Message */ | ||
| 118 | msgs[0].addr = mpu_addr; | ||
| 119 | msgs[0].flags = 0; | ||
| 120 | msgs[0].buf = bank; | ||
| 121 | msgs[0].len = sizeof(bank); | ||
| 122 | |||
| 123 | msgs[1].addr = mpu_addr; | ||
| 124 | msgs[1].flags = 0; | ||
| 125 | msgs[1].buf = addr; | ||
| 126 | msgs[1].len = sizeof(addr); | ||
| 127 | |||
| 128 | msgs[2].addr = mpu_addr; | ||
| 129 | msgs[2].flags = 0; | ||
| 130 | msgs[2].buf = &buf; | ||
| 131 | msgs[2].len = 1; | ||
| 132 | |||
| 133 | msgs[3].addr = mpu_addr; | ||
| 134 | msgs[3].flags = I2C_M_RD; | ||
| 135 | msgs[3].buf = data; | ||
| 136 | msgs[3].len = len; | ||
| 137 | |||
| 138 | ret = i2c_transfer(i2c_adap, msgs, 4); | ||
| 139 | if (ret != 4) | ||
| 140 | return ret; | ||
| 141 | else | ||
| 142 | return 0; | ||
| 143 | } | ||
| 144 | |||
| 145 | int mpu_memory_write(struct i2c_adapter *i2c_adap, | ||
| 146 | unsigned char mpu_addr, | ||
| 147 | unsigned short mem_addr, | ||
| 148 | unsigned int len, unsigned char const *data) | ||
| 149 | { | ||
| 150 | unsigned char bank[2]; | ||
| 151 | unsigned char addr[2]; | ||
| 152 | unsigned char buf[513]; | ||
| 153 | |||
| 154 | struct i2c_msg msgs[3]; | ||
| 155 | int ret; | ||
| 156 | |||
| 157 | if (NULL == data || NULL == i2c_adap) | ||
| 158 | return -EINVAL; | ||
| 159 | if (len >= (sizeof(buf) - 1)) | ||
| 160 | return -ENOMEM; | ||
| 161 | |||
| 162 | bank[0] = MPUREG_BANK_SEL; | ||
| 163 | bank[1] = mem_addr >> 8; | ||
| 164 | |||
| 165 | addr[0] = MPUREG_MEM_START_ADDR; | ||
| 166 | addr[1] = mem_addr & 0xFF; | ||
| 167 | |||
| 168 | buf[0] = MPUREG_MEM_R_W; | ||
| 169 | memcpy(buf + 1, data, len); | ||
| 170 | |||
| 171 | /* Write Message */ | ||
| 172 | msgs[0].addr = mpu_addr; | ||
| 173 | msgs[0].flags = 0; | ||
| 174 | msgs[0].buf = bank; | ||
| 175 | msgs[0].len = sizeof(bank); | ||
| 176 | |||
| 177 | msgs[1].addr = mpu_addr; | ||
| 178 | msgs[1].flags = 0; | ||
| 179 | msgs[1].buf = addr; | ||
| 180 | msgs[1].len = sizeof(addr); | ||
| 181 | |||
| 182 | msgs[2].addr = mpu_addr; | ||
| 183 | msgs[2].flags = 0; | ||
| 184 | msgs[2].buf = (unsigned char *) buf; | ||
| 185 | msgs[2].len = len + 1; | ||
| 186 | |||
| 187 | ret = i2c_transfer(i2c_adap, msgs, 3); | ||
| 188 | if (ret != 3) | ||
| 189 | return ret; | ||
| 190 | else | ||
| 191 | return 0; | ||
| 192 | } | ||
| 193 | |||
| 194 | /** | ||
| 195 | * @} | ||
| 196 | */ | ||
diff --git a/drivers/misc/mpu3050/mpu-i2c.h b/drivers/misc/mpu3050/mpu-i2c.h new file mode 100644 index 00000000000..0bbc8c64594 --- /dev/null +++ b/drivers/misc/mpu3050/mpu-i2c.h | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2010 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | /** | ||
| 20 | * @defgroup | ||
| 21 | * @brief | ||
| 22 | * | ||
| 23 | * @{ | ||
| 24 | * @file mpu-i2c.c | ||
| 25 | * @brief | ||
| 26 | * | ||
| 27 | * | ||
| 28 | */ | ||
| 29 | |||
| 30 | #ifndef __MPU_I2C_H__ | ||
| 31 | #define __MPU_I2C_H__ | ||
| 32 | |||
| 33 | #include <linux/i2c.h> | ||
| 34 | |||
| 35 | int sensor_i2c_write(struct i2c_adapter *i2c_adap, | ||
| 36 | unsigned char address, | ||
| 37 | unsigned int len, unsigned char const *data); | ||
| 38 | |||
| 39 | int sensor_i2c_write_register(struct i2c_adapter *i2c_adap, | ||
| 40 | unsigned char address, | ||
| 41 | unsigned char reg, unsigned char value); | ||
| 42 | |||
| 43 | int sensor_i2c_read(struct i2c_adapter *i2c_adap, | ||
| 44 | unsigned char address, | ||
| 45 | unsigned char reg, | ||
| 46 | unsigned int len, unsigned char *data); | ||
| 47 | |||
| 48 | int mpu_memory_read(struct i2c_adapter *i2c_adap, | ||
| 49 | unsigned char mpu_addr, | ||
| 50 | unsigned short mem_addr, | ||
| 51 | unsigned int len, unsigned char *data); | ||
| 52 | |||
| 53 | int mpu_memory_write(struct i2c_adapter *i2c_adap, | ||
| 54 | unsigned char mpu_addr, | ||
| 55 | unsigned short mem_addr, | ||
| 56 | unsigned int len, unsigned char const *data); | ||
| 57 | |||
| 58 | #endif /* __MPU_I2C_H__ */ | ||
diff --git a/drivers/misc/mpu3050/mpuirq.c b/drivers/misc/mpu3050/mpuirq.c new file mode 100644 index 00000000000..ce1ad409cbf --- /dev/null +++ b/drivers/misc/mpu3050/mpuirq.c | |||
| @@ -0,0 +1,319 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2010 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | #include <linux/interrupt.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/moduleparam.h> | ||
| 22 | #include <linux/kernel.h> | ||
| 23 | #include <linux/init.h> | ||
| 24 | #include <linux/stat.h> | ||
| 25 | #include <linux/irq.h> | ||
| 26 | #include <linux/signal.h> | ||
| 27 | #include <linux/miscdevice.h> | ||
| 28 | #include <linux/i2c.h> | ||
| 29 | #include <linux/i2c-dev.h> | ||
| 30 | #include <linux/workqueue.h> | ||
| 31 | #include <linux/poll.h> | ||
| 32 | |||
| 33 | #include <linux/errno.h> | ||
| 34 | #include <linux/fs.h> | ||
| 35 | #include <linux/mm.h> | ||
| 36 | #include <linux/sched.h> | ||
| 37 | #include <linux/wait.h> | ||
| 38 | #include <linux/uaccess.h> | ||
| 39 | #include <linux/io.h> | ||
| 40 | |||
| 41 | #include "mpu.h" | ||
| 42 | #include "mpuirq.h" | ||
| 43 | #include "mldl_cfg.h" | ||
| 44 | #include "mpu-i2c.h" | ||
| 45 | |||
| 46 | #define MPUIRQ_NAME "mpuirq" | ||
| 47 | |||
| 48 | /* function which gets accel data and sends it to MPU */ | ||
| 49 | |||
| 50 | DECLARE_WAIT_QUEUE_HEAD(mpuirq_wait); | ||
| 51 | |||
| 52 | struct mpuirq_dev_data { | ||
| 53 | struct work_struct work; | ||
| 54 | struct i2c_client *mpu_client; | ||
| 55 | struct miscdevice *dev; | ||
| 56 | int irq; | ||
| 57 | int pid; | ||
| 58 | int accel_divider; | ||
| 59 | int data_ready; | ||
| 60 | int timeout; | ||
| 61 | }; | ||
| 62 | |||
| 63 | static struct mpuirq_dev_data mpuirq_dev_data; | ||
| 64 | static struct mpuirq_data mpuirq_data; | ||
| 65 | static char *interface = MPUIRQ_NAME; | ||
| 66 | |||
| 67 | static void mpu_accel_data_work_fcn(struct work_struct *work); | ||
| 68 | |||
| 69 | static int mpuirq_open(struct inode *inode, struct file *file) | ||
| 70 | { | ||
| 71 | dev_dbg(mpuirq_dev_data.dev->this_device, | ||
| 72 | "%s current->pid %d\n", __func__, current->pid); | ||
| 73 | mpuirq_dev_data.pid = current->pid; | ||
| 74 | file->private_data = &mpuirq_dev_data; | ||
| 75 | return 0; | ||
| 76 | } | ||
| 77 | |||
| 78 | /* close function - called when the "file" /dev/mpuirq is closed in userspace */ | ||
| 79 | static int mpuirq_release(struct inode *inode, struct file *file) | ||
| 80 | { | ||
| 81 | dev_dbg(mpuirq_dev_data.dev->this_device, "mpuirq_release\n"); | ||
| 82 | return 0; | ||
| 83 | } | ||
| 84 | |||
| 85 | /* read function called when from /dev/mpuirq is read */ | ||
| 86 | static ssize_t mpuirq_read(struct file *file, | ||
| 87 | char *buf, size_t count, loff_t *ppos) | ||
| 88 | { | ||
| 89 | int len, err; | ||
| 90 | struct mpuirq_dev_data *p_mpuirq_dev_data = file->private_data; | ||
| 91 | |||
| 92 | if (!mpuirq_dev_data.data_ready && | ||
| 93 | mpuirq_dev_data.timeout && | ||
| 94 | (!(file->f_flags & O_NONBLOCK))) { | ||
| 95 | wait_event_interruptible_timeout(mpuirq_wait, | ||
| 96 | mpuirq_dev_data. | ||
| 97 | data_ready, | ||
| 98 | mpuirq_dev_data.timeout); | ||
| 99 | } | ||
| 100 | |||
| 101 | if (mpuirq_dev_data.data_ready && NULL != buf | ||
| 102 | && count >= sizeof(mpuirq_data)) { | ||
| 103 | err = copy_to_user(buf, &mpuirq_data, sizeof(mpuirq_data)); | ||
| 104 | mpuirq_data.data_type = 0; | ||
| 105 | } else { | ||
| 106 | return 0; | ||
| 107 | } | ||
| 108 | if (err != 0) { | ||
| 109 | dev_err(p_mpuirq_dev_data->dev->this_device, | ||
| 110 | "Copy to user returned %d\n", err); | ||
| 111 | return -EFAULT; | ||
| 112 | } | ||
| 113 | mpuirq_dev_data.data_ready = 0; | ||
| 114 | len = sizeof(mpuirq_data); | ||
| 115 | return len; | ||
| 116 | } | ||
| 117 | |||
| 118 | unsigned int mpuirq_poll(struct file *file, struct poll_table_struct *poll) | ||
| 119 | { | ||
| 120 | int mask = 0; | ||
| 121 | |||
| 122 | poll_wait(file, &mpuirq_wait, poll); | ||
| 123 | if (mpuirq_dev_data.data_ready) | ||
| 124 | mask |= POLLIN | POLLRDNORM; | ||
| 125 | return mask; | ||
| 126 | } | ||
| 127 | |||
| 128 | /* ioctl - I/O control */ | ||
| 129 | static long mpuirq_ioctl(struct file *file, | ||
| 130 | unsigned int cmd, unsigned long arg) | ||
| 131 | { | ||
| 132 | int retval = 0; | ||
| 133 | int data; | ||
| 134 | |||
| 135 | switch (cmd) { | ||
| 136 | case MPUIRQ_SET_TIMEOUT: | ||
| 137 | mpuirq_dev_data.timeout = arg; | ||
| 138 | break; | ||
| 139 | |||
| 140 | case MPUIRQ_GET_INTERRUPT_CNT: | ||
| 141 | data = mpuirq_data.interruptcount - 1; | ||
| 142 | if (mpuirq_data.interruptcount > 1) | ||
| 143 | mpuirq_data.interruptcount = 1; | ||
| 144 | |||
| 145 | if (copy_to_user((int *) arg, &data, sizeof(int))) | ||
| 146 | return -EFAULT; | ||
| 147 | break; | ||
| 148 | case MPUIRQ_GET_IRQ_TIME: | ||
| 149 | if (copy_to_user((int *) arg, &mpuirq_data.irqtime, | ||
| 150 | sizeof(mpuirq_data.irqtime))) | ||
| 151 | return -EFAULT; | ||
| 152 | mpuirq_data.irqtime = 0; | ||
| 153 | break; | ||
| 154 | case MPUIRQ_SET_FREQUENCY_DIVIDER: | ||
| 155 | mpuirq_dev_data.accel_divider = arg; | ||
| 156 | break; | ||
| 157 | default: | ||
| 158 | retval = -EINVAL; | ||
| 159 | } | ||
| 160 | return retval; | ||
| 161 | } | ||
| 162 | |||
| 163 | static void mpu_accel_data_work_fcn(struct work_struct *work) | ||
| 164 | { | ||
| 165 | struct mpuirq_dev_data *mpuirq_dev_data = | ||
| 166 | (struct mpuirq_dev_data *) work; | ||
| 167 | struct mldl_cfg *mldl_cfg = | ||
| 168 | (struct mldl_cfg *) | ||
| 169 | i2c_get_clientdata(mpuirq_dev_data->mpu_client); | ||
| 170 | struct i2c_adapter *accel_adapter; | ||
| 171 | unsigned char wbuff[16]; | ||
| 172 | unsigned char rbuff[16]; | ||
| 173 | int ii; | ||
| 174 | |||
| 175 | accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num); | ||
| 176 | mldl_cfg->accel->read(accel_adapter, | ||
| 177 | mldl_cfg->accel, | ||
| 178 | &mldl_cfg->pdata->accel, rbuff); | ||
| 179 | |||
| 180 | |||
| 181 | /* @todo add other data formats here as well */ | ||
| 182 | if (EXT_SLAVE_BIG_ENDIAN == mldl_cfg->accel->endian) { | ||
| 183 | for (ii = 0; ii < 3; ii++) { | ||
| 184 | wbuff[2 * ii + 1] = rbuff[2 * ii + 1]; | ||
| 185 | wbuff[2 * ii + 2] = rbuff[2 * ii + 0]; | ||
| 186 | } | ||
| 187 | } else { | ||
| 188 | memcpy(wbuff + 1, rbuff, mldl_cfg->accel->len); | ||
| 189 | } | ||
| 190 | |||
| 191 | wbuff[7] = 0; | ||
| 192 | wbuff[8] = 1; /*set semaphore */ | ||
| 193 | |||
| 194 | mpu_memory_write(mpuirq_dev_data->mpu_client->adapter, | ||
| 195 | mldl_cfg->addr, 0x0108, 8, wbuff); | ||
| 196 | } | ||
| 197 | |||
| 198 | static irqreturn_t mpuirq_handler(int irq, void *dev_id) | ||
| 199 | { | ||
| 200 | static int mycount; | ||
| 201 | struct timeval irqtime; | ||
| 202 | mycount++; | ||
| 203 | |||
| 204 | mpuirq_data.interruptcount++; | ||
| 205 | |||
| 206 | /* wake up (unblock) for reading data from userspace */ | ||
| 207 | /* and ignore first interrupt generated in module init */ | ||
| 208 | mpuirq_dev_data.data_ready = 1; | ||
| 209 | |||
| 210 | do_gettimeofday(&irqtime); | ||
| 211 | mpuirq_data.irqtime = (((long long) irqtime.tv_sec) << 32); | ||
| 212 | mpuirq_data.irqtime += irqtime.tv_usec; | ||
| 213 | |||
| 214 | if ((mpuirq_dev_data.accel_divider >= 0) && | ||
| 215 | (0 == (mycount % (mpuirq_dev_data.accel_divider + 1)))) { | ||
| 216 | schedule_work((struct work_struct | ||
| 217 | *) (&mpuirq_dev_data)); | ||
| 218 | } | ||
| 219 | |||
| 220 | wake_up_interruptible(&mpuirq_wait); | ||
| 221 | |||
| 222 | return IRQ_HANDLED; | ||
| 223 | |||
| 224 | } | ||
| 225 | |||
| 226 | /* define which file operations are supported */ | ||
| 227 | const struct file_operations mpuirq_fops = { | ||
| 228 | .owner = THIS_MODULE, | ||
| 229 | .read = mpuirq_read, | ||
| 230 | .poll = mpuirq_poll, | ||
| 231 | |||
| 232 | #if HAVE_COMPAT_IOCTL | ||
| 233 | .compat_ioctl = mpuirq_ioctl, | ||
| 234 | #endif | ||
| 235 | #if HAVE_UNLOCKED_IOCTL | ||
| 236 | .unlocked_ioctl = mpuirq_ioctl, | ||
| 237 | #endif | ||
| 238 | .open = mpuirq_open, | ||
| 239 | .release = mpuirq_release, | ||
| 240 | }; | ||
| 241 | |||
| 242 | static struct miscdevice mpuirq_device = { | ||
| 243 | .minor = MISC_DYNAMIC_MINOR, | ||
| 244 | .name = MPUIRQ_NAME, | ||
| 245 | .fops = &mpuirq_fops, | ||
| 246 | }; | ||
| 247 | |||
| 248 | int mpuirq_init(struct i2c_client *mpu_client) | ||
| 249 | { | ||
| 250 | |||
| 251 | int res; | ||
| 252 | struct mldl_cfg *mldl_cfg = | ||
| 253 | (struct mldl_cfg *) i2c_get_clientdata(mpu_client); | ||
| 254 | |||
| 255 | /* work_struct initialization */ | ||
| 256 | INIT_WORK((struct work_struct *) &mpuirq_dev_data, | ||
| 257 | mpu_accel_data_work_fcn); | ||
| 258 | mpuirq_dev_data.mpu_client = mpu_client; | ||
| 259 | |||
| 260 | dev_info(&mpu_client->adapter->dev, | ||
| 261 | "Module Param interface = %s\n", interface); | ||
| 262 | |||
| 263 | mpuirq_dev_data.irq = mpu_client->irq; | ||
| 264 | mpuirq_dev_data.pid = 0; | ||
| 265 | mpuirq_dev_data.accel_divider = -1; | ||
| 266 | mpuirq_dev_data.data_ready = 0; | ||
| 267 | mpuirq_dev_data.timeout = 0; | ||
| 268 | mpuirq_dev_data.dev = &mpuirq_device; | ||
| 269 | |||
| 270 | if (mpuirq_dev_data.irq) { | ||
| 271 | unsigned long flags; | ||
| 272 | if (BIT_ACTL_LOW == | ||
| 273 | ((mldl_cfg->pdata->int_config) & BIT_ACTL)) | ||
| 274 | flags = IRQF_TRIGGER_FALLING; | ||
| 275 | else | ||
| 276 | flags = IRQF_TRIGGER_RISING; | ||
| 277 | |||
| 278 | res = | ||
| 279 | request_irq(mpuirq_dev_data.irq, mpuirq_handler, flags, | ||
| 280 | interface, &mpuirq_dev_data.irq); | ||
| 281 | if (res) { | ||
| 282 | dev_err(&mpu_client->adapter->dev, | ||
| 283 | "myirqtest: cannot register IRQ %d\n", | ||
| 284 | mpuirq_dev_data.irq); | ||
| 285 | } else { | ||
| 286 | res = misc_register(&mpuirq_device); | ||
| 287 | if (res < 0) { | ||
| 288 | dev_err(&mpu_client->adapter->dev, | ||
| 289 | "misc_register returned %d\n", | ||
| 290 | res); | ||
| 291 | free_irq(mpuirq_dev_data.irq, | ||
| 292 | &mpuirq_dev_data.irq); | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | } else { | ||
| 297 | res = 0; | ||
| 298 | } | ||
| 299 | |||
| 300 | return res; | ||
| 301 | } | ||
| 302 | |||
| 303 | void mpuirq_exit(void) | ||
| 304 | { | ||
| 305 | /* Free the IRQ first before flushing the work */ | ||
| 306 | if (mpuirq_dev_data.irq > 0) | ||
| 307 | free_irq(mpuirq_dev_data.irq, &mpuirq_dev_data.irq); | ||
| 308 | |||
| 309 | flush_scheduled_work(); | ||
| 310 | |||
| 311 | dev_info(mpuirq_device.this_device, "Unregistering %s\n", | ||
| 312 | MPUIRQ_NAME); | ||
| 313 | misc_deregister(&mpuirq_device); | ||
| 314 | |||
| 315 | return; | ||
| 316 | } | ||
| 317 | |||
| 318 | module_param(interface, charp, S_IRUGO | S_IWUSR); | ||
| 319 | MODULE_PARM_DESC(interface, "The Interface name"); | ||
diff --git a/drivers/misc/mpu3050/mpuirq.h b/drivers/misc/mpu3050/mpuirq.h new file mode 100644 index 00000000000..a71c79c75e8 --- /dev/null +++ b/drivers/misc/mpu3050/mpuirq.h | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2010 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __MPUIRQ__ | ||
| 21 | #define __MPUIRQ__ | ||
| 22 | |||
| 23 | #ifdef __KERNEL__ | ||
| 24 | #include <linux/i2c-dev.h> | ||
| 25 | #include <linux/time.h> | ||
| 26 | #else | ||
| 27 | #include <sys/time.h> | ||
| 28 | #endif | ||
| 29 | |||
| 30 | #define MPUIRQ_SET_TIMEOUT _IOW(MPU_IOCTL, 0x40, unsigned long) | ||
| 31 | #define MPUIRQ_GET_INTERRUPT_CNT _IOR(MPU_IOCTL, 0x41, unsigned long) | ||
| 32 | #define MPUIRQ_GET_IRQ_TIME _IOR(MPU_IOCTL, 0x42, struct timeval) | ||
| 33 | #define MPUIRQ_SET_FREQUENCY_DIVIDER _IOW(MPU_IOCTL, 0x43, unsigned long) | ||
| 34 | |||
| 35 | #ifdef __KERNEL__ | ||
| 36 | |||
| 37 | void mpuirq_exit(void); | ||
| 38 | int mpuirq_init(struct i2c_client *mpu_client); | ||
| 39 | |||
| 40 | #endif | ||
| 41 | |||
| 42 | #endif | ||
diff --git a/drivers/misc/mpu3050/slaveirq.c b/drivers/misc/mpu3050/slaveirq.c new file mode 100644 index 00000000000..a3c7bfec4b4 --- /dev/null +++ b/drivers/misc/mpu3050/slaveirq.c | |||
| @@ -0,0 +1,273 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2010 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | #include <linux/interrupt.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/moduleparam.h> | ||
| 22 | #include <linux/kernel.h> | ||
| 23 | #include <linux/init.h> | ||
| 24 | #include <linux/stat.h> | ||
| 25 | #include <linux/irq.h> | ||
| 26 | #include <linux/signal.h> | ||
| 27 | #include <linux/miscdevice.h> | ||
| 28 | #include <linux/i2c.h> | ||
| 29 | #include <linux/i2c-dev.h> | ||
| 30 | #include <linux/poll.h> | ||
| 31 | |||
| 32 | #include <linux/errno.h> | ||
| 33 | #include <linux/fs.h> | ||
| 34 | #include <linux/mm.h> | ||
| 35 | #include <linux/sched.h> | ||
| 36 | #include <linux/wait.h> | ||
| 37 | #include <linux/uaccess.h> | ||
| 38 | #include <linux/io.h> | ||
| 39 | #include <linux/wait.h> | ||
| 40 | #include <linux/slab.h> | ||
| 41 | |||
| 42 | #include "mpu.h" | ||
| 43 | #include "slaveirq.h" | ||
| 44 | #include "mldl_cfg.h" | ||
| 45 | #include "mpu-i2c.h" | ||
| 46 | |||
| 47 | /* function which gets slave data and sends it to SLAVE */ | ||
| 48 | |||
| 49 | struct slaveirq_dev_data { | ||
| 50 | struct miscdevice dev; | ||
| 51 | struct i2c_client *slave_client; | ||
| 52 | struct mpuirq_data data; | ||
| 53 | wait_queue_head_t slaveirq_wait; | ||
| 54 | int irq; | ||
| 55 | int pid; | ||
| 56 | int data_ready; | ||
| 57 | int timeout; | ||
| 58 | }; | ||
| 59 | |||
| 60 | /* The following depends on patch fa1f68db6ca7ebb6fc4487ac215bffba06c01c28 | ||
| 61 | * drivers: misc: pass miscdevice pointer via file private data | ||
| 62 | */ | ||
| 63 | static int slaveirq_open(struct inode *inode, struct file *file) | ||
| 64 | { | ||
| 65 | /* Device node is availabe in the file->private_data, this is | ||
| 66 | * exactly what we want so we leave it there */ | ||
| 67 | struct slaveirq_dev_data *data = | ||
| 68 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
| 69 | |||
| 70 | dev_dbg(data->dev.this_device, | ||
| 71 | "%s current->pid %d\n", __func__, current->pid); | ||
| 72 | data->pid = current->pid; | ||
| 73 | return 0; | ||
| 74 | } | ||
| 75 | |||
| 76 | static int slaveirq_release(struct inode *inode, struct file *file) | ||
| 77 | { | ||
| 78 | struct slaveirq_dev_data *data = | ||
| 79 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
| 80 | dev_dbg(data->dev.this_device, "slaveirq_release\n"); | ||
| 81 | return 0; | ||
| 82 | } | ||
| 83 | |||
| 84 | /* read function called when from /dev/slaveirq is read */ | ||
| 85 | static ssize_t slaveirq_read(struct file *file, | ||
| 86 | char *buf, size_t count, loff_t *ppos) | ||
| 87 | { | ||
| 88 | int len, err; | ||
| 89 | struct slaveirq_dev_data *data = | ||
| 90 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
| 91 | |||
| 92 | if (!data->data_ready && | ||
| 93 | data->timeout && | ||
| 94 | !(file->f_flags & O_NONBLOCK)) { | ||
| 95 | wait_event_interruptible_timeout(data->slaveirq_wait, | ||
| 96 | data->data_ready, | ||
| 97 | data->timeout); | ||
| 98 | } | ||
| 99 | |||
| 100 | if (data->data_ready && NULL != buf | ||
| 101 | && count >= sizeof(data->data)) { | ||
| 102 | err = copy_to_user(buf, &data->data, sizeof(data->data)); | ||
| 103 | data->data.data_type = 0; | ||
| 104 | } else { | ||
| 105 | return 0; | ||
| 106 | } | ||
| 107 | if (err != 0) { | ||
| 108 | dev_err(data->dev.this_device, | ||
| 109 | "Copy to user returned %d\n", err); | ||
| 110 | return -EFAULT; | ||
| 111 | } | ||
| 112 | data->data_ready = 0; | ||
| 113 | len = sizeof(data->data); | ||
| 114 | return len; | ||
| 115 | } | ||
| 116 | |||
| 117 | static unsigned int slaveirq_poll(struct file *file, | ||
| 118 | struct poll_table_struct *poll) | ||
| 119 | { | ||
| 120 | int mask = 0; | ||
| 121 | struct slaveirq_dev_data *data = | ||
| 122 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
| 123 | |||
| 124 | poll_wait(file, &data->slaveirq_wait, poll); | ||
| 125 | if (data->data_ready) | ||
| 126 | mask |= POLLIN | POLLRDNORM; | ||
| 127 | return mask; | ||
| 128 | } | ||
| 129 | |||
| 130 | /* ioctl - I/O control */ | ||
| 131 | static long slaveirq_ioctl(struct file *file, | ||
| 132 | unsigned int cmd, unsigned long arg) | ||
| 133 | { | ||
| 134 | int retval = 0; | ||
| 135 | int tmp; | ||
| 136 | struct slaveirq_dev_data *data = | ||
| 137 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
| 138 | |||
| 139 | switch (cmd) { | ||
| 140 | case SLAVEIRQ_SET_TIMEOUT: | ||
| 141 | data->timeout = arg; | ||
| 142 | break; | ||
| 143 | |||
| 144 | case SLAVEIRQ_GET_INTERRUPT_CNT: | ||
| 145 | tmp = data->data.interruptcount - 1; | ||
| 146 | if (data->data.interruptcount > 1) | ||
| 147 | data->data.interruptcount = 1; | ||
| 148 | |||
| 149 | if (copy_to_user((int *) arg, &tmp, sizeof(int))) | ||
| 150 | return -EFAULT; | ||
| 151 | break; | ||
| 152 | case SLAVEIRQ_GET_IRQ_TIME: | ||
| 153 | if (copy_to_user((int *) arg, &data->data.irqtime, | ||
| 154 | sizeof(data->data.irqtime))) | ||
| 155 | return -EFAULT; | ||
| 156 | data->data.irqtime = 0; | ||
| 157 | break; | ||
| 158 | default: | ||
| 159 | retval = -EINVAL; | ||
| 160 | } | ||
| 161 | return retval; | ||
| 162 | } | ||
| 163 | |||
| 164 | static irqreturn_t slaveirq_handler(int irq, void *dev_id) | ||
| 165 | { | ||
| 166 | struct slaveirq_dev_data *data = (struct slaveirq_dev_data *)dev_id; | ||
| 167 | static int mycount; | ||
| 168 | struct timeval irqtime; | ||
| 169 | mycount++; | ||
| 170 | |||
| 171 | data->data.interruptcount++; | ||
| 172 | |||
| 173 | /* wake up (unblock) for reading data from userspace */ | ||
| 174 | data->data_ready = 1; | ||
| 175 | |||
| 176 | do_gettimeofday(&irqtime); | ||
| 177 | data->data.irqtime = (((long long) irqtime.tv_sec) << 32); | ||
| 178 | data->data.irqtime += irqtime.tv_usec; | ||
| 179 | data->data.data_type |= 1; | ||
| 180 | |||
| 181 | wake_up_interruptible(&data->slaveirq_wait); | ||
| 182 | |||
| 183 | return IRQ_HANDLED; | ||
| 184 | |||
| 185 | } | ||
| 186 | |||
| 187 | /* define which file operations are supported */ | ||
| 188 | static const struct file_operations slaveirq_fops = { | ||
| 189 | .owner = THIS_MODULE, | ||
| 190 | .read = slaveirq_read, | ||
| 191 | .poll = slaveirq_poll, | ||
| 192 | |||
| 193 | #if HAVE_COMPAT_IOCTL | ||
| 194 | .compat_ioctl = slaveirq_ioctl, | ||
| 195 | #endif | ||
| 196 | #if HAVE_UNLOCKED_IOCTL | ||
| 197 | .unlocked_ioctl = slaveirq_ioctl, | ||
| 198 | #endif | ||
| 199 | .open = slaveirq_open, | ||
| 200 | .release = slaveirq_release, | ||
| 201 | }; | ||
| 202 | |||
| 203 | int slaveirq_init(struct i2c_adapter *slave_adapter, | ||
| 204 | struct ext_slave_platform_data *pdata, | ||
| 205 | char *name) | ||
| 206 | { | ||
| 207 | |||
| 208 | int res; | ||
| 209 | struct slaveirq_dev_data *data; | ||
| 210 | |||
| 211 | if (!pdata->irq) | ||
| 212 | return -EINVAL; | ||
| 213 | |||
| 214 | pdata->irq_data = kzalloc(sizeof(*data), | ||
| 215 | GFP_KERNEL); | ||
| 216 | data = (struct slaveirq_dev_data *) pdata->irq_data; | ||
| 217 | if (!data) | ||
| 218 | return -ENOMEM; | ||
| 219 | |||
| 220 | data->dev.minor = MISC_DYNAMIC_MINOR; | ||
| 221 | data->dev.name = name; | ||
| 222 | data->dev.fops = &slaveirq_fops; | ||
| 223 | data->irq = pdata->irq; | ||
| 224 | data->pid = 0; | ||
| 225 | data->data_ready = 0; | ||
| 226 | data->timeout = 0; | ||
| 227 | |||
| 228 | init_waitqueue_head(&data->slaveirq_wait); | ||
| 229 | |||
| 230 | res = request_irq(data->irq, slaveirq_handler, IRQF_TRIGGER_RISING, | ||
| 231 | data->dev.name, data); | ||
| 232 | |||
| 233 | if (res) { | ||
| 234 | dev_err(&slave_adapter->dev, | ||
| 235 | "myirqtest: cannot register IRQ %d\n", | ||
| 236 | data->irq); | ||
| 237 | goto out_request_irq; | ||
| 238 | } | ||
| 239 | |||
| 240 | res = misc_register(&data->dev); | ||
| 241 | if (res < 0) { | ||
| 242 | dev_err(&slave_adapter->dev, | ||
| 243 | "misc_register returned %d\n", | ||
| 244 | res); | ||
| 245 | goto out_misc_register; | ||
| 246 | } | ||
| 247 | |||
| 248 | return res; | ||
| 249 | |||
| 250 | out_misc_register: | ||
| 251 | free_irq(data->irq, data); | ||
| 252 | out_request_irq: | ||
| 253 | kfree(pdata->irq_data); | ||
| 254 | pdata->irq_data = NULL; | ||
| 255 | |||
| 256 | return res; | ||
| 257 | } | ||
| 258 | |||
| 259 | void slaveirq_exit(struct ext_slave_platform_data *pdata) | ||
| 260 | { | ||
| 261 | struct slaveirq_dev_data *data = pdata->irq_data; | ||
| 262 | |||
| 263 | if (!pdata->irq_data || data->irq <= 0) | ||
| 264 | return; | ||
| 265 | |||
| 266 | dev_info(data->dev.this_device, "Unregistering %s\n", | ||
| 267 | data->dev.name); | ||
| 268 | |||
| 269 | free_irq(data->irq, data); | ||
| 270 | misc_deregister(&data->dev); | ||
| 271 | kfree(pdata->irq_data); | ||
| 272 | pdata->irq_data = NULL; | ||
| 273 | } | ||
diff --git a/drivers/misc/mpu3050/slaveirq.h b/drivers/misc/mpu3050/slaveirq.h new file mode 100644 index 00000000000..b4e1115f1b0 --- /dev/null +++ b/drivers/misc/mpu3050/slaveirq.h | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2010 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __SLAVEIRQ__ | ||
| 21 | #define __SLAVEIRQ__ | ||
| 22 | |||
| 23 | #ifdef __KERNEL__ | ||
| 24 | #include <linux/i2c-dev.h> | ||
| 25 | #endif | ||
| 26 | |||
| 27 | #include "mpu.h" | ||
| 28 | #include "mpuirq.h" | ||
| 29 | |||
| 30 | #define SLAVEIRQ_SET_TIMEOUT _IOW(MPU_IOCTL, 0x50, unsigned long) | ||
| 31 | #define SLAVEIRQ_GET_INTERRUPT_CNT _IOR(MPU_IOCTL, 0x51, unsigned long) | ||
| 32 | #define SLAVEIRQ_GET_IRQ_TIME _IOR(MPU_IOCTL, 0x52, unsigned long) | ||
| 33 | |||
| 34 | #ifdef __KERNEL__ | ||
| 35 | |||
| 36 | void slaveirq_exit(struct ext_slave_platform_data *pdata); | ||
| 37 | int slaveirq_init(struct i2c_adapter *slave_adapter, | ||
| 38 | struct ext_slave_platform_data *pdata, | ||
| 39 | char *name); | ||
| 40 | |||
| 41 | #endif | ||
| 42 | |||
| 43 | #endif | ||
diff --git a/drivers/misc/mpu3050/timerirq.c b/drivers/misc/mpu3050/timerirq.c new file mode 100644 index 00000000000..41c3ac98101 --- /dev/null +++ b/drivers/misc/mpu3050/timerirq.c | |||
| @@ -0,0 +1,299 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2010 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | #include <linux/interrupt.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/moduleparam.h> | ||
| 22 | #include <linux/kernel.h> | ||
| 23 | #include <linux/init.h> | ||
| 24 | #include <linux/stat.h> | ||
| 25 | #include <linux/signal.h> | ||
| 26 | #include <linux/miscdevice.h> | ||
| 27 | #include <linux/i2c.h> | ||
| 28 | #include <linux/i2c-dev.h> | ||
| 29 | #include <linux/poll.h> | ||
| 30 | |||
| 31 | #include <linux/errno.h> | ||
| 32 | #include <linux/fs.h> | ||
| 33 | #include <linux/mm.h> | ||
| 34 | #include <linux/sched.h> | ||
| 35 | #include <linux/wait.h> | ||
| 36 | #include <linux/uaccess.h> | ||
| 37 | #include <linux/io.h> | ||
| 38 | #include <linux/timer.h> | ||
| 39 | #include <linux/slab.h> | ||
| 40 | |||
| 41 | #include "mpu.h" | ||
| 42 | #include "mltypes.h" | ||
| 43 | #include "timerirq.h" | ||
| 44 | |||
| 45 | /* function which gets timer data and sends it to TIMER */ | ||
| 46 | struct timerirq_data { | ||
| 47 | int pid; | ||
| 48 | int data_ready; | ||
| 49 | int run; | ||
| 50 | int timeout; | ||
| 51 | unsigned long period; | ||
| 52 | struct mpuirq_data data; | ||
| 53 | struct completion timer_done; | ||
| 54 | wait_queue_head_t timerirq_wait; | ||
| 55 | struct timer_list timer; | ||
| 56 | struct miscdevice *dev; | ||
| 57 | }; | ||
| 58 | |||
| 59 | static struct miscdevice *timerirq_dev_data; | ||
| 60 | |||
| 61 | static void timerirq_handler(unsigned long arg) | ||
| 62 | { | ||
| 63 | struct timerirq_data *data = (struct timerirq_data *)arg; | ||
| 64 | struct timeval irqtime; | ||
| 65 | |||
| 66 | data->data.interruptcount++; | ||
| 67 | |||
| 68 | data->data_ready = 1; | ||
| 69 | |||
| 70 | do_gettimeofday(&irqtime); | ||
| 71 | data->data.irqtime = (((long long) irqtime.tv_sec) << 32); | ||
| 72 | data->data.irqtime += irqtime.tv_usec; | ||
| 73 | data->data.data_type |= 1; | ||
| 74 | |||
| 75 | dev_dbg(data->dev->this_device, | ||
| 76 | "%s, %lld, %ld\n", __func__, data->data.irqtime, | ||
| 77 | (unsigned long)data); | ||
| 78 | |||
| 79 | wake_up_interruptible(&data->timerirq_wait); | ||
| 80 | |||
| 81 | if (data->run) | ||
| 82 | mod_timer(&data->timer, | ||
| 83 | jiffies + msecs_to_jiffies(data->period)); | ||
| 84 | else | ||
| 85 | complete(&data->timer_done); | ||
| 86 | } | ||
| 87 | |||
| 88 | static int start_timerirq(struct timerirq_data *data) | ||
| 89 | { | ||
| 90 | dev_dbg(data->dev->this_device, | ||
| 91 | "%s current->pid %d\n", __func__, current->pid); | ||
| 92 | |||
| 93 | /* Timer already running... success */ | ||
| 94 | if (data->run) | ||
| 95 | return 0; | ||
| 96 | |||
| 97 | /* Don't allow a period of 0 since this would fire constantly */ | ||
| 98 | if (!data->period) | ||
| 99 | return -EINVAL; | ||
| 100 | |||
| 101 | data->run = TRUE; | ||
| 102 | data->data_ready = FALSE; | ||
| 103 | |||
| 104 | init_completion(&data->timer_done); | ||
| 105 | setup_timer(&data->timer, timerirq_handler, (unsigned long)data); | ||
| 106 | |||
| 107 | return mod_timer(&data->timer, | ||
| 108 | jiffies + msecs_to_jiffies(data->period)); | ||
| 109 | } | ||
| 110 | |||
| 111 | static int stop_timerirq(struct timerirq_data *data) | ||
| 112 | { | ||
| 113 | dev_dbg(data->dev->this_device, | ||
| 114 | "%s current->pid %lx\n", __func__, (unsigned long)data); | ||
| 115 | |||
| 116 | if (data->run) { | ||
| 117 | data->run = FALSE; | ||
| 118 | mod_timer(&data->timer, jiffies + 1); | ||
| 119 | wait_for_completion(&data->timer_done); | ||
| 120 | } | ||
| 121 | return 0; | ||
| 122 | } | ||
| 123 | |||
| 124 | /* The following depends on patch fa1f68db6ca7ebb6fc4487ac215bffba06c01c28 | ||
| 125 | * drivers: misc: pass miscdevice pointer via file private data | ||
| 126 | */ | ||
| 127 | static int timerirq_open(struct inode *inode, struct file *file) | ||
| 128 | { | ||
| 129 | /* Device node is availabe in the file->private_data, this is | ||
| 130 | * exactly what we want so we leave it there */ | ||
| 131 | struct miscdevice *dev_data = file->private_data; | ||
| 132 | struct timerirq_data *data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
| 133 | if (!data) | ||
| 134 | return -ENOMEM; | ||
| 135 | |||
| 136 | data->dev = dev_data; | ||
| 137 | file->private_data = data; | ||
| 138 | data->pid = current->pid; | ||
| 139 | init_waitqueue_head(&data->timerirq_wait); | ||
| 140 | |||
| 141 | dev_dbg(data->dev->this_device, | ||
| 142 | "%s current->pid %d\n", __func__, current->pid); | ||
| 143 | return 0; | ||
| 144 | } | ||
| 145 | |||
| 146 | static int timerirq_release(struct inode *inode, struct file *file) | ||
| 147 | { | ||
| 148 | struct timerirq_data *data = file->private_data; | ||
| 149 | dev_dbg(data->dev->this_device, "timerirq_release\n"); | ||
| 150 | if (data->run) | ||
| 151 | stop_timerirq(data); | ||
| 152 | kfree(data); | ||
| 153 | return 0; | ||
| 154 | } | ||
| 155 | |||
| 156 | /* read function called when from /dev/timerirq is read */ | ||
| 157 | static ssize_t timerirq_read(struct file *file, | ||
| 158 | char *buf, size_t count, loff_t *ppos) | ||
| 159 | { | ||
| 160 | int len, err; | ||
| 161 | struct timerirq_data *data = file->private_data; | ||
| 162 | |||
| 163 | if (!data->data_ready && | ||
| 164 | data->timeout && | ||
| 165 | !(file->f_flags & O_NONBLOCK)) { | ||
| 166 | wait_event_interruptible_timeout(data->timerirq_wait, | ||
| 167 | data->data_ready, | ||
| 168 | data->timeout); | ||
| 169 | } | ||
| 170 | |||
| 171 | if (data->data_ready && NULL != buf | ||
| 172 | && count >= sizeof(data->data)) { | ||
| 173 | err = copy_to_user(buf, &data->data, sizeof(data->data)); | ||
| 174 | data->data.data_type = 0; | ||
| 175 | } else { | ||
| 176 | return 0; | ||
| 177 | } | ||
| 178 | if (err != 0) { | ||
| 179 | dev_err(data->dev->this_device, | ||
| 180 | "Copy to user returned %d\n", err); | ||
| 181 | return -EFAULT; | ||
| 182 | } | ||
| 183 | data->data_ready = 0; | ||
| 184 | len = sizeof(data->data); | ||
| 185 | return len; | ||
| 186 | } | ||
| 187 | |||
| 188 | static unsigned int timerirq_poll(struct file *file, | ||
| 189 | struct poll_table_struct *poll) | ||
| 190 | { | ||
| 191 | int mask = 0; | ||
| 192 | struct timerirq_data *data = file->private_data; | ||
| 193 | |||
| 194 | poll_wait(file, &data->timerirq_wait, poll); | ||
| 195 | if (data->data_ready) | ||
| 196 | mask |= POLLIN | POLLRDNORM; | ||
| 197 | return mask; | ||
| 198 | } | ||
| 199 | |||
| 200 | /* ioctl - I/O control */ | ||
| 201 | static long timerirq_ioctl(struct file *file, | ||
| 202 | unsigned int cmd, unsigned long arg) | ||
| 203 | { | ||
| 204 | int retval = 0; | ||
| 205 | int tmp; | ||
| 206 | struct timerirq_data *data = file->private_data; | ||
| 207 | |||
| 208 | dev_dbg(data->dev->this_device, | ||
| 209 | "%s current->pid %d, %d, %ld\n", | ||
| 210 | __func__, current->pid, cmd, arg); | ||
| 211 | |||
| 212 | if (!data) | ||
| 213 | return -EFAULT; | ||
| 214 | |||
| 215 | switch (cmd) { | ||
| 216 | case TIMERIRQ_SET_TIMEOUT: | ||
| 217 | data->timeout = arg; | ||
| 218 | break; | ||
| 219 | case TIMERIRQ_GET_INTERRUPT_CNT: | ||
| 220 | tmp = data->data.interruptcount - 1; | ||
| 221 | if (data->data.interruptcount > 1) | ||
| 222 | data->data.interruptcount = 1; | ||
| 223 | |||
| 224 | if (copy_to_user((int *) arg, &tmp, sizeof(int))) | ||
| 225 | return -EFAULT; | ||
| 226 | break; | ||
| 227 | case TIMERIRQ_START: | ||
| 228 | data->period = arg; | ||
| 229 | retval = start_timerirq(data); | ||
| 230 | break; | ||
| 231 | case TIMERIRQ_STOP: | ||
| 232 | retval = stop_timerirq(data); | ||
| 233 | break; | ||
| 234 | default: | ||
| 235 | retval = -EINVAL; | ||
| 236 | } | ||
| 237 | return retval; | ||
| 238 | } | ||
| 239 | |||
| 240 | /* define which file operations are supported */ | ||
| 241 | static const struct file_operations timerirq_fops = { | ||
| 242 | .owner = THIS_MODULE, | ||
| 243 | .read = timerirq_read, | ||
| 244 | .poll = timerirq_poll, | ||
| 245 | |||
| 246 | #if HAVE_COMPAT_IOCTL | ||
| 247 | .compat_ioctl = timerirq_ioctl, | ||
| 248 | #endif | ||
| 249 | #if HAVE_UNLOCKED_IOCTL | ||
| 250 | .unlocked_ioctl = timerirq_ioctl, | ||
| 251 | #endif | ||
| 252 | .open = timerirq_open, | ||
| 253 | .release = timerirq_release, | ||
| 254 | }; | ||
| 255 | |||
| 256 | static int __init timerirq_init(void) | ||
| 257 | { | ||
| 258 | |||
| 259 | int res; | ||
| 260 | static struct miscdevice *data; | ||
| 261 | |||
| 262 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
| 263 | if (!data) | ||
| 264 | return -ENOMEM; | ||
| 265 | timerirq_dev_data = data; | ||
| 266 | data->minor = MISC_DYNAMIC_MINOR; | ||
| 267 | data->name = "timerirq"; | ||
| 268 | data->fops = &timerirq_fops; | ||
| 269 | |||
| 270 | res = misc_register(data); | ||
| 271 | if (res < 0) { | ||
| 272 | dev_err(data->this_device, | ||
| 273 | "misc_register returned %d\n", | ||
| 274 | res); | ||
| 275 | return res; | ||
| 276 | } | ||
| 277 | |||
| 278 | return res; | ||
| 279 | } | ||
| 280 | module_init(timerirq_init); | ||
| 281 | |||
| 282 | static void __exit timerirq_exit(void) | ||
| 283 | { | ||
| 284 | struct miscdevice *data = timerirq_dev_data; | ||
| 285 | |||
| 286 | dev_info(data->this_device, "Unregistering %s\n", | ||
| 287 | data->name); | ||
| 288 | |||
| 289 | misc_deregister(data); | ||
| 290 | kfree(data); | ||
| 291 | |||
| 292 | timerirq_dev_data = NULL; | ||
| 293 | } | ||
| 294 | module_exit(timerirq_exit); | ||
| 295 | |||
| 296 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 297 | MODULE_DESCRIPTION("Timer IRQ device driver."); | ||
| 298 | MODULE_LICENSE("GPL"); | ||
| 299 | MODULE_ALIAS("timerirq"); | ||
diff --git a/drivers/misc/mpu3050/timerirq.h b/drivers/misc/mpu3050/timerirq.h new file mode 100644 index 00000000000..ec2c1e29f08 --- /dev/null +++ b/drivers/misc/mpu3050/timerirq.h | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2010 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __TIMERIRQ__ | ||
| 21 | #define __TIMERIRQ__ | ||
| 22 | |||
| 23 | #include "mpu.h" | ||
| 24 | |||
| 25 | #define TIMERIRQ_SET_TIMEOUT _IOW(MPU_IOCTL, 0x60, unsigned long) | ||
| 26 | #define TIMERIRQ_GET_INTERRUPT_CNT _IOW(MPU_IOCTL, 0x61, unsigned long) | ||
| 27 | #define TIMERIRQ_START _IOW(MPU_IOCTL, 0x62, unsigned long) | ||
| 28 | #define TIMERIRQ_STOP _IO(MPU_IOCTL, 0x63) | ||
| 29 | |||
| 30 | #endif | ||
