| /* |
| * Copyright (C) 2017-2018 InvenSense, Inc. |
| * |
| * This software is licensed under the terms of the GNU General Public |
| * License version 2, as published by the Free Software Foundation, and |
| * may be copied, distributed, and modified under those terms. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| #define pr_fmt(fmt) "inv_mpu: " fmt |
| |
| #include <linux/module.h> |
| #include <linux/init.h> |
| #include <linux/slab.h> |
| #include <linux/err.h> |
| #include <linux/delay.h> |
| #include <linux/sysfs.h> |
| #include <linux/jiffies.h> |
| #include <linux/irq.h> |
| #include <linux/interrupt.h> |
| #include <linux/kfifo.h> |
| #include <linux/poll.h> |
| #include <linux/miscdevice.h> |
| #include <linux/spinlock.h> |
| #include <linux/spi/spi.h> |
| #include <linux/i2c.h> |
| |
| #include "../inv_mpu_iio.h" |
| |
| static const struct inv_hw_s hw_info[INV_NUM_PARTS] = { |
| {128, "ICM20608D"}, |
| {128, "ICM20690"}, |
| {128, "ICM20602"}, |
| {128, "IAM20680"}, |
| }; |
| |
| #ifndef SUPPORT_ONLY_BASIC_FEATURES |
| static char debug_reg_addr = 0x6; |
| #endif |
| |
| const char sensor_l_info[][30] = { |
| "SENSOR_L_ACCEL", |
| "SENSOR_L_GYRO", |
| "SENSOR_L_MAG", |
| "SENSOR_L_ALS", |
| "SENSOR_L_SIXQ", |
| "SENSOR_L_THREEQ", |
| "SENSOR_L_NINEQ", |
| "SENSOR_L_PEDQ", |
| "SENSOR_L_GEOMAG", |
| "SENSOR_L_PRESSURE", |
| "SENSOR_L_GYRO_CAL", |
| "SENSOR_L_MAG_CAL", |
| "SENSOR_L_EIS_GYRO", |
| "SENSOR_L_ACCEL_WAKE", |
| "SENSOR_L_GYRO_WAKE", |
| "SENSOR_L_MAG_WAKE", |
| "SENSOR_L_ALS_WAKE", |
| "SENSOR_L_SIXQ_WAKE", |
| "SENSOR_L_NINEQ_WAKE", |
| "SENSOR_L_PEDQ_WAKE", |
| "SENSOR_L_GEOMAG_WAKE", |
| "SENSOR_L_PRESSURE_WAKE", |
| "SENSOR_L_GYRO_CAL_WAKE", |
| "SENSOR_L_MAG_CAL_WAKE", |
| "SENSOR_L_NUM_MAX", |
| }; |
| |
| static int inv_set_accel_bias_reg(struct inv_mpu_state *st, |
| int accel_bias, int axis) |
| { |
| int accel_reg_bias; |
| u8 addr; |
| u8 d[2]; |
| int result = 0; |
| |
| switch (axis) { |
| case 0: |
| /* X */ |
| addr = REG_XA_OFFS_H; |
| break; |
| case 1: |
| /* Y */ |
| addr = REG_YA_OFFS_H; |
| break; |
| case 2: |
| /* Z* */ |
| addr = REG_ZA_OFFS_H; |
| break; |
| default: |
| result = -EINVAL; |
| goto accel_bias_set_err; |
| } |
| |
| result = inv_plat_read(st, addr, 2, d); |
| if (result) |
| goto accel_bias_set_err; |
| accel_reg_bias = ((int)d[0] << 8) | d[1]; |
| |
| /* accel_bias is 2g scaled by 1<<16. |
| * Convert to 16g, and mask bit0 */ |
| accel_reg_bias -= ((accel_bias / 8 / 65536) & ~1); |
| |
| d[0] = (accel_reg_bias >> 8) & 0xff; |
| d[1] = (accel_reg_bias) & 0xff; |
| result = inv_plat_single_write(st, addr, d[0]); |
| if (result) |
| goto accel_bias_set_err; |
| result = inv_plat_single_write(st, addr + 1, d[1]); |
| if (result) |
| goto accel_bias_set_err; |
| |
| accel_bias_set_err: |
| return result; |
| } |
| |
| static int inv_set_gyro_bias_reg(struct inv_mpu_state *st, |
| const int gyro_bias, int axis) |
| { |
| int gyro_reg_bias; |
| u8 addr; |
| u8 d[2]; |
| int result = 0; |
| |
| switch (axis) { |
| case 0: |
| /* X */ |
| addr = REG_XG_OFFS_USR_H; |
| break; |
| case 1: |
| /* Y */ |
| addr = REG_YG_OFFS_USR_H; |
| break; |
| case 2: |
| /* Z */ |
| addr = REG_ZG_OFFS_USR_H; |
| break; |
| default: |
| result = -EINVAL; |
| goto gyro_bias_set_err; |
| } |
| |
| /* gyro_bias is 2000dps scaled by 1<<16. |
| * Convert to 1000dps */ |
| gyro_reg_bias = (-gyro_bias * 2 / 65536); |
| |
| d[0] = (gyro_reg_bias >> 8) & 0xff; |
| d[1] = (gyro_reg_bias) & 0xff; |
| result = inv_plat_single_write(st, addr, d[0]); |
| if (result) |
| goto gyro_bias_set_err; |
| result = inv_plat_single_write(st, addr + 1, d[1]); |
| if (result) |
| goto gyro_bias_set_err; |
| |
| gyro_bias_set_err: |
| return result; |
| } |
| |
| static int _bias_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, |
| size_t count) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct inv_mpu_state *st = iio_priv(indio_dev); |
| struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); |
| int result, data; |
| |
| result = inv_switch_power_in_lp(st, true); |
| if (result) |
| return result; |
| |
| result = kstrtoint(buf, 10, &data); |
| if (result) |
| goto bias_store_fail; |
| switch (this_attr->address) { |
| case ATTR_ACCEL_X_OFFSET: |
| result = inv_set_accel_bias_reg(st, data, 0); |
| if (result) |
| goto bias_store_fail; |
| st->input_accel_bias[0] = data; |
| break; |
| case ATTR_ACCEL_Y_OFFSET: |
| result = inv_set_accel_bias_reg(st, data, 1); |
| if (result) |
| goto bias_store_fail; |
| st->input_accel_bias[1] = data; |
| break; |
| case ATTR_ACCEL_Z_OFFSET: |
| result = inv_set_accel_bias_reg(st, data, 2); |
| if (result) |
| goto bias_store_fail; |
| st->input_accel_bias[2] = data; |
| break; |
| case ATTR_GYRO_X_OFFSET: |
| result = inv_set_gyro_bias_reg(st, data, 0); |
| if (result) |
| goto bias_store_fail; |
| st->input_gyro_bias[0] = data; |
| break; |
| case ATTR_GYRO_Y_OFFSET: |
| result = inv_set_gyro_bias_reg(st, data, 1); |
| if (result) |
| goto bias_store_fail; |
| st->input_gyro_bias[1] = data; |
| break; |
| case ATTR_GYRO_Z_OFFSET: |
| result = inv_set_gyro_bias_reg(st, data, 2); |
| if (result) |
| goto bias_store_fail; |
| st->input_gyro_bias[2] = data; |
| break; |
| default: |
| break; |
| } |
| |
| bias_store_fail: |
| if (result) |
| return result; |
| result = inv_switch_power_in_lp(st, false); |
| if (result) |
| return result; |
| |
| return count; |
| } |
| |
| static ssize_t inv_bias_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, |
| size_t count) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| int result; |
| |
| mutex_lock(&indio_dev->mlock); |
| result = _bias_store(dev, attr, buf, count); |
| mutex_unlock(&indio_dev->mlock); |
| |
| return result; |
| } |
| |
| #ifndef SUPPORT_ONLY_BASIC_FEATURES |
| static ssize_t inv_debug_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, |
| size_t count) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct inv_mpu_state *st = iio_priv(indio_dev); |
| struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); |
| int result, data; |
| |
| result = kstrtoint(buf, 10, &data); |
| if (result) |
| return result; |
| switch (this_attr->address) { |
| case ATTR_DMP_LP_EN_OFF: |
| st->chip_config.lp_en_mode_off = !!data; |
| inv_switch_power_in_lp(st, !!data); |
| break; |
| case ATTR_DMP_CLK_SEL: |
| st->chip_config.clk_sel = !!data; |
| inv_switch_power_in_lp(st, !!data); |
| break; |
| case ATTR_DEBUG_REG_ADDR: |
| debug_reg_addr = data; |
| break; |
| case ATTR_DEBUG_REG_WRITE: |
| inv_plat_single_write(st, debug_reg_addr, data); |
| break; |
| } |
| return count; |
| } |
| #endif |
| |
| static int _misc_attr_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, |
| size_t count) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct inv_mpu_state *st = iio_priv(indio_dev); |
| struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); |
| int result, data; |
| |
| result = inv_switch_power_in_lp(st, true); |
| if (result) |
| return result; |
| result = kstrtoint(buf, 10, &data); |
| if (result) |
| return result; |
| switch (this_attr->address) { |
| case ATTR_GYRO_SCALE: |
| if (data > 3) |
| return -EINVAL; |
| st->chip_config.fsr = data; |
| result = inv_set_gyro_sf(st); |
| return result; |
| case ATTR_ACCEL_SCALE: |
| if (data > 3) |
| return -EINVAL; |
| st->chip_config.accel_fs = data; |
| result = inv_set_accel_sf(st); |
| return result; |
| default: |
| return -EINVAL; |
| } |
| st->trigger_state = MISC_TRIGGER; |
| result = set_inv_enable(indio_dev); |
| |
| return result; |
| } |
| |
| #ifdef CONFIG_ENABLE_IAM_ACC_GYRO_BUFFERING |
| static inline int inv_check_acc_gyro_early_buff_enable_flag( |
| struct iio_dev *indio_dev) |
| { |
| struct inv_mpu_state *st = iio_priv(indio_dev); |
| |
| if (st->acc_buffer_inv_samples == true || |
| st->gyro_buffer_inv_samples == true) |
| return 1; |
| else |
| return 0; |
| } |
| #else |
| static inline int inv_check_acc_gyro_early_buff_enable_flag( |
| struct iio_dev *indio_dev) |
| { |
| return 0; |
| } |
| #endif |
| |
| /* |
| * inv_misc_attr_store() - calling this function |
| */ |
| static ssize_t inv_misc_attr_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, |
| size_t count) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| int result; |
| |
| if (inv_check_acc_gyro_early_buff_enable_flag(indio_dev)) |
| return count; |
| |
| mutex_lock(&indio_dev->mlock); |
| result = _misc_attr_store(dev, attr, buf, count); |
| mutex_unlock(&indio_dev->mlock); |
| if (result) |
| return result; |
| |
| return count; |
| } |
| |
| static ssize_t inv_sensor_rate_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct inv_mpu_state *st = iio_priv(indio_dev); |
| struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); |
| |
| return snprintf(buf, MAX_WR_SZ, "%d\n", |
| st->sensor_l[this_attr->address].rate); |
| } |
| |
| static ssize_t inv_sensor_rate_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, |
| size_t count) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct inv_mpu_state *st = iio_priv(indio_dev); |
| struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); |
| int data, rate, ind; |
| int result; |
| |
| if (inv_check_acc_gyro_early_buff_enable_flag(indio_dev)) |
| return count; |
| |
| result = kstrtoint(buf, 10, &data); |
| if (result) |
| return -EINVAL; |
| if (data <= 0) { |
| pr_err("sensor_rate_store: invalid data=%d\n", data); |
| return -EINVAL; |
| } |
| ind = this_attr->address; |
| rate = inv_rate_convert(st, ind, data); |
| |
| pr_debug("sensor [%s] requested rate %d input [%d]\n", |
| sensor_l_info[ind], rate, data); |
| |
| if (rate == st->sensor_l[ind].rate) |
| return count; |
| mutex_lock(&indio_dev->mlock); |
| st->sensor_l[ind].rate = rate; |
| st->trigger_state = DATA_TRIGGER; |
| inv_check_sensor_on(st); |
| result = set_inv_enable(indio_dev); |
| pr_debug("%s rate %d div %d\n", sensor_l_info[ind], |
| st->sensor_l[ind].rate, st->sensor_l[ind].div); |
| mutex_unlock(&indio_dev->mlock); |
| |
| return count; |
| } |
| |
| static ssize_t inv_sensor_on_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct inv_mpu_state *st = iio_priv(indio_dev); |
| struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); |
| |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->sensor_l[this_attr->address].on); |
| } |
| |
| static ssize_t inv_sensor_on_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, |
| size_t count) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct inv_mpu_state *st = iio_priv(indio_dev); |
| struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); |
| int data, on, ind; |
| int result; |
| |
| if (inv_check_acc_gyro_early_buff_enable_flag(indio_dev)) |
| return count; |
| |
| result = kstrtoint(buf, 10, &data); |
| if (result) |
| return -EINVAL; |
| if (data < 0) { |
| pr_err("sensor_on_store: invalid data=%d\n", data); |
| return -EINVAL; |
| } |
| ind = this_attr->address; |
| on = !!data; |
| |
| pr_debug("sensor [%s] requested %s, input [%d]\n", |
| sensor_l_info[ind], (on == 1) ? "On" : "Off", data); |
| |
| if (on == st->sensor_l[ind].on) { |
| pr_debug("sensor [%s] is already %s, input [%d]\n", |
| sensor_l_info[ind], (on == 1) ? "On" : "Off", data); |
| return count; |
| } |
| |
| mutex_lock(&indio_dev->mlock); |
| st->sensor_l[ind].on = on; |
| st->trigger_state = RATE_TRIGGER; |
| inv_check_sensor_on(st); |
| result = set_inv_enable(indio_dev); |
| mutex_unlock(&indio_dev->mlock); |
| if (result) |
| return result; |
| |
| pr_debug("Sensor [%s] is %s by sysfs\n", |
| sensor_l_info[ind], (on == 1) ? "On" : "Off"); |
| return count; |
| } |
| |
| static int inv_check_l_step(struct inv_mpu_state *st) |
| { |
| if (st->step_counter_l_on || st->step_counter_wake_l_on) |
| st->ped.on = true; |
| else |
| st->ped.on = false; |
| |
| return 0; |
| } |
| |
| static int _basic_attr_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, |
| size_t count) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct inv_mpu_state *st = iio_priv(indio_dev); |
| struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); |
| int data; |
| int result; |
| u32 power_on_data; |
| |
| result = kstrtoint(buf, 10, &data); |
| if (result || (data < 0)) |
| return -EINVAL; |
| |
| switch (this_attr->address) { |
| case ATTR_DMP_PED_ON: |
| if ((!!data) == st->ped.on) |
| return count; |
| st->ped.on = !!data; |
| break; |
| case ATTR_DMP_TILT_ENABLE: |
| if ((!!data) == st->chip_config.tilt_enable) |
| return count; |
| st->chip_config.tilt_enable = !!data; |
| pr_info("Tile %s\n", |
| st->chip_config.tilt_enable == |
| 1 ? "Enabled" : "Disabled"); |
| break; |
| case ATTR_DMP_PICK_UP_ENABLE: |
| if ((!!data) == st->chip_config.pick_up_enable) { |
| pr_info("Pick_up enable already %s\n", |
| st->chip_config.pick_up_enable == |
| 1 ? "Enabled" : "Disabled"); |
| return count; |
| } |
| st->chip_config.pick_up_enable = !!data; |
| pr_info("Pick up %s\n", |
| st->chip_config.pick_up_enable == |
| 1 ? "Enable" : "Disable"); |
| break; |
| case ATTR_IN_POWER_ON: |
| { |
| u8 p0[2]; |
| u8 p1[2]; |
| |
| power_on_data = (u32)data; |
| p0[0] = (power_on_data & 0xff); |
| p0[1] = ((power_on_data >> 8) & 0xff); |
| p1[0] = ((power_on_data >> 16) & 0xff); |
| p1[1] = ((power_on_data >> 24) & 0xff); |
| |
| if (st->bus_type == BUS_IIO_SPI) { |
| struct spi_transfer power_on; |
| struct spi_message msg; |
| |
| memset(&power_on, 0, sizeof(struct spi_transfer)); |
| |
| power_on.bits_per_word = 8; |
| power_on.len = 2; |
| |
| power_on.tx_buf = p0; |
| power_on.rx_buf = p1; |
| spi_message_init(&msg); |
| spi_message_add_tail(&power_on, &msg); |
| spi_sync(to_spi_device(st->dev), &msg); |
| |
| } else if (st->bus_type == BUS_IIO_I2C) { |
| struct i2c_msg msgs[2]; |
| |
| p0[0] &= 0x7f; |
| |
| msgs[0].addr = st->i2c_addr; |
| msgs[0].flags = 0; /* write */ |
| msgs[0].buf = &p0[0]; |
| msgs[0].len = 1; |
| |
| msgs[1].addr = st->i2c_addr; |
| msgs[1].flags = I2C_M_RD; |
| msgs[1].buf = &p1[1]; |
| msgs[1].len = 1; |
| |
| result = i2c_transfer(st->sl_handle, msgs, 2); |
| if (result < 2) |
| return -EIO; |
| } |
| st->power_on_data = ((p0[0] << 24) | (p0[1] << 16) | |
| (p1[0] << 8) | p1[1]); |
| return count; |
| } |
| case ATTR_DMP_EIS_ENABLE: |
| if ((!!data) == st->chip_config.eis_enable) |
| return count; |
| st->chip_config.eis_enable = !!data; |
| pr_info("Eis %s\n", |
| st->chip_config.eis_enable == 1 ? "Enable" : "Disable"); |
| break; |
| case ATTR_DMP_STEP_DETECTOR_ON: |
| st->step_detector_l_on = !!data; |
| break; |
| case ATTR_DMP_STEP_DETECTOR_WAKE_ON: |
| st->step_detector_wake_l_on = !!data; |
| break; |
| case ATTR_DMP_STEP_COUNTER_ON: |
| st->step_counter_l_on = !!data; |
| break; |
| case ATTR_DMP_STEP_COUNTER_WAKE_ON: |
| st->step_counter_wake_l_on = !!data; |
| break; |
| case ATTR_DMP_BATCHMODE_TIMEOUT: |
| if (data == st->batch.timeout) |
| return count; |
| st->batch.timeout = data; |
| break; |
| default: |
| return -EINVAL; |
| }; |
| inv_check_l_step(st); |
| inv_check_sensor_on(st); |
| |
| st->trigger_state = EVENT_TRIGGER; |
| result = set_inv_enable(indio_dev); |
| if (result) |
| return result; |
| |
| return count; |
| } |
| |
| /* |
| * inv_basic_attr_store() |
| */ |
| static ssize_t inv_basic_attr_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, |
| size_t count) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| int result; |
| |
| if (inv_check_acc_gyro_early_buff_enable_flag(indio_dev)) |
| return count; |
| |
| mutex_lock(&indio_dev->mlock); |
| result = _basic_attr_store(dev, attr, buf, count); |
| |
| mutex_unlock(&indio_dev->mlock); |
| |
| return result; |
| } |
| |
| /* |
| * inv_attr_show() |
| */ |
| static ssize_t inv_attr_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct inv_mpu_state *st = iio_priv(indio_dev); |
| struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); |
| s8 *m; |
| |
| switch (this_attr->address) { |
| case ATTR_GYRO_SCALE: |
| { |
| const s16 gyro_scale[] = { 250, 500, 1000, 2000 }; |
| |
| return snprintf(buf, MAX_WR_SZ, "%d\n", |
| gyro_scale[st->chip_config.fsr]); |
| } |
| case ATTR_ACCEL_SCALE: |
| { |
| const s16 accel_scale[] = { 2, 4, 8, 16 }; |
| return snprintf(buf, MAX_WR_SZ, "%d\n", |
| accel_scale[st->chip_config.accel_fs]); |
| } |
| case ATTR_GYRO_ENABLE: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->chip_config.gyro_enable); |
| case ATTR_ACCEL_ENABLE: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->chip_config.accel_enable); |
| case ATTR_IN_POWER_ON: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->power_on_data); |
| case ATTR_DMP_BATCHMODE_TIMEOUT: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->batch.timeout); |
| case ATTR_DMP_PED_ON: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->ped.on); |
| case ATTR_DMP_TILT_ENABLE: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", |
| st->chip_config.tilt_enable); |
| case ATTR_DMP_PICK_UP_ENABLE: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", |
| st->chip_config.pick_up_enable); |
| case ATTR_DMP_EIS_ENABLE: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->chip_config.eis_enable); |
| case ATTR_DMP_LP_EN_OFF: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", |
| st->chip_config.lp_en_mode_off); |
| case ATTR_DMP_STEP_COUNTER_ON: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->step_counter_l_on); |
| case ATTR_DMP_STEP_COUNTER_WAKE_ON: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->step_counter_wake_l_on); |
| case ATTR_DMP_STEP_DETECTOR_ON: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->step_detector_l_on); |
| case ATTR_DMP_STEP_DETECTOR_WAKE_ON: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->step_detector_wake_l_on); |
| case ATTR_GYRO_MATRIX: |
| m = st->plat_data.orientation; |
| return snprintf(buf, MAX_WR_SZ, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n", |
| m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], |
| m[8]); |
| case ATTR_ACCEL_MATRIX: |
| m = st->plat_data.orientation; |
| return snprintf(buf, MAX_WR_SZ, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n", |
| m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], |
| m[8]); |
| case ATTR_GYRO_SF: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->gyro_sf); |
| case ATTR_ANGLVEL_X_ST_CALIBBIAS: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->gyro_st_bias[0]); |
| case ATTR_ANGLVEL_Y_ST_CALIBBIAS: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->gyro_st_bias[1]); |
| case ATTR_ANGLVEL_Z_ST_CALIBBIAS: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->gyro_st_bias[2]); |
| case ATTR_ACCEL_X_ST_CALIBBIAS: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->accel_st_bias[0]); |
| case ATTR_ACCEL_Y_ST_CALIBBIAS: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->accel_st_bias[1]); |
| case ATTR_ACCEL_Z_ST_CALIBBIAS: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->accel_st_bias[2]); |
| case ATTR_GYRO_X_OFFSET: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->input_gyro_bias[0]); |
| case ATTR_GYRO_Y_OFFSET: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->input_gyro_bias[1]); |
| case ATTR_GYRO_Z_OFFSET: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->input_gyro_bias[2]); |
| case ATTR_ACCEL_X_OFFSET: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->input_accel_bias[0]); |
| case ATTR_ACCEL_Y_OFFSET: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->input_accel_bias[1]); |
| case ATTR_ACCEL_Z_OFFSET: |
| return snprintf(buf, MAX_WR_SZ, "%d\n", st->input_accel_bias[2]); |
| default: |
| return -EPERM; |
| } |
| } |
| |
| static ssize_t inv_self_test(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct inv_mpu_state *st = iio_priv(indio_dev); |
| int res; |
| |
| mutex_lock(&indio_dev->mlock); |
| res = inv_hw_self_test(st); |
| set_inv_enable(indio_dev); |
| mutex_unlock(&indio_dev->mlock); |
| |
| return snprintf(buf, MAX_WR_SZ, "%d\n", res); |
| } |
| |
| |
| /* |
| * inv_temperature_show() - Read temperature data directly from registers. |
| */ |
| static ssize_t inv_temperature_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct inv_mpu_state *st = iio_priv(indio_dev); |
| |
| u8 data[2]; |
| s32 temp; |
| int res; |
| |
| mutex_lock(&indio_dev->mlock); |
| res = inv_plat_read(st, REG_RAW_TEMP, 2, data); |
| if (res) |
| return res; |
| mutex_unlock(&indio_dev->mlock); |
| |
| temp = (s16)be16_to_cpup((__be16 *)(data)) * 10000; |
| temp = temp / TEMP_SENSITIVITY + TEMP_OFFSET; |
| |
| return snprintf(buf, MAX_WR_SZ, "%d %lld\n", temp, get_time_ns()); |
| } |
| |
| /* |
| * inv_reg_dump_show() - Register dump for testing. |
| */ |
| static ssize_t inv_reg_dump_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| int ii; |
| char data; |
| int bytes_printed = 0; |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct inv_mpu_state *st = iio_priv(indio_dev); |
| |
| mutex_lock(&indio_dev->mlock); |
| bytes_printed += snprintf(buf + bytes_printed, MAX_WR_SZ, "bank 0\n"); |
| |
| for (ii = 0; ii < 0x7F; ii++) { |
| /* don't read fifo r/w register */ |
| if ((ii == REG_MEM_R_W) || (ii == REG_FIFO_R_W)) |
| data = 0; |
| else |
| inv_plat_read(st, ii, 1, &data); |
| bytes_printed += snprintf(buf + bytes_printed, MAX_WR_SZ, |
| "%#2x: %#2x\n", ii, data); |
| } |
| set_inv_enable(indio_dev); |
| mutex_unlock(&indio_dev->mlock); |
| |
| return bytes_printed; |
| } |
| |
| static ssize_t inv_flush_batch_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, |
| size_t count) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| int result, data; |
| |
| result = kstrtoint(buf, 10, &data); |
| if (result) |
| return result; |
| |
| mutex_lock(&indio_dev->mlock); |
| result = inv_flush_batch_data(indio_dev, data); |
| mutex_unlock(&indio_dev->mlock); |
| |
| return count; |
| } |
| |
| #ifdef CONFIG_ENABLE_IAM_ACC_GYRO_BUFFERING |
| static int inv_gyro_read_bootsampl(struct inv_mpu_state *st, |
| unsigned long enable_read) |
| { |
| int i = 0; |
| |
| if (enable_read) { |
| st->gyro_buffer_inv_samples = false; |
| for (i = 0; i < st->gyro_bufsample_cnt; i++) { |
| dev_dbg(st->dev, "gyro_cnt=%d,x=%d,y=%d,z=%d,tsec=%d,nsec=%lld\n", |
| i, st->inv_gyro_samplist[i]->xyz[0], |
| st->inv_gyro_samplist[i]->xyz[1], |
| st->inv_gyro_samplist[i]->xyz[2], |
| st->inv_gyro_samplist[i]->tsec, |
| st->inv_gyro_samplist[i]->tnsec); |
| input_report_abs(st->gyrobuf_dev, ABS_X, |
| st->inv_gyro_samplist[i]->xyz[0]); |
| input_report_abs(st->gyrobuf_dev, ABS_Y, |
| st->inv_gyro_samplist[i]->xyz[1]); |
| input_report_abs(st->gyrobuf_dev, ABS_Z, |
| st->inv_gyro_samplist[i]->xyz[2]); |
| input_report_abs(st->gyrobuf_dev, ABS_RX, |
| st->inv_gyro_samplist[i]->tsec); |
| input_report_abs(st->gyrobuf_dev, ABS_RY, |
| st->inv_gyro_samplist[i]->tnsec); |
| input_sync(st->gyrobuf_dev); |
| } |
| } else { |
| /* clean up */ |
| if (st->gyro_bufsample_cnt != 0) { |
| for (i = 0; i < INV_GYRO_MAXSAMPLE; i++) |
| kmem_cache_free(st->inv_gyro_cachepool, |
| st->inv_gyro_samplist[i]); |
| kmem_cache_destroy(st->inv_gyro_cachepool); |
| st->gyro_bufsample_cnt = 0; |
| } |
| |
| } |
| /*SYN_CONFIG indicates end of data*/ |
| input_event(st->gyrobuf_dev, EV_SYN, SYN_CONFIG, 0xFFFFFFFF); |
| input_sync(st->gyrobuf_dev); |
| dev_dbg(st->dev, "End of gyro samples bufsample_cnt=%d\n", |
| st->gyro_bufsample_cnt); |
| return 0; |
| } |
| static int inv_acc_read_bootsampl(struct inv_mpu_state *st, |
| unsigned long enable_read) |
| { |
| int i = 0; |
| |
| if (enable_read) { |
| st->acc_buffer_inv_samples = false; |
| for (i = 0; i < st->acc_bufsample_cnt; i++) { |
| dev_dbg(st->dev, "acc_cnt=%d,x=%d,y=%d,z=%d,tsec=%d,nsec=%lld\n", |
| i, st->inv_acc_samplist[i]->xyz[0], |
| st->inv_acc_samplist[i]->xyz[1], |
| st->inv_acc_samplist[i]->xyz[2], |
| st->inv_acc_samplist[i]->tsec, |
| st->inv_acc_samplist[i]->tnsec); |
| input_report_abs(st->accbuf_dev, ABS_X, |
| st->inv_acc_samplist[i]->xyz[0]); |
| input_report_abs(st->accbuf_dev, ABS_Y, |
| st->inv_acc_samplist[i]->xyz[1]); |
| input_report_abs(st->accbuf_dev, ABS_Z, |
| st->inv_acc_samplist[i]->xyz[2]); |
| input_report_abs(st->accbuf_dev, ABS_RX, |
| st->inv_acc_samplist[i]->tsec); |
| input_report_abs(st->accbuf_dev, ABS_RY, |
| st->inv_acc_samplist[i]->tnsec); |
| input_sync(st->accbuf_dev); |
| } |
| } else { |
| /* clean up */ |
| if (st->acc_bufsample_cnt != 0) { |
| for (i = 0; i < INV_ACC_MAXSAMPLE; i++) |
| kmem_cache_free(st->inv_acc_cachepool, |
| st->inv_acc_samplist[i]); |
| kmem_cache_destroy(st->inv_acc_cachepool); |
| st->acc_bufsample_cnt = 0; |
| } |
| |
| } |
| /*SYN_CONFIG indicates end of data*/ |
| input_event(st->accbuf_dev, EV_SYN, SYN_CONFIG, 0xFFFFFFFF); |
| input_sync(st->accbuf_dev); |
| dev_dbg(st->dev, "End of acc samples bufsample_cnt=%d\n", |
| st->acc_bufsample_cnt); |
| return 0; |
| } |
| |
| static ssize_t read_gyro_boot_sample_show(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct inv_mpu_state *st = iio_priv(indio_dev); |
| |
| return snprintf(buf, MAX_WR_SZ, "%d\n", |
| st->read_gyro_boot_sample); |
| } |
| |
| static ssize_t read_gyro_boot_sample_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| int err; |
| unsigned long enable = 0; |
| |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct inv_mpu_state *st = iio_priv(indio_dev); |
| |
| err = kstrtoul(buf, 10, &enable); |
| if (err) |
| return err; |
| if (enable > 1) { |
| dev_err(st->dev, |
| "Invalid value of input, input=%ld\n", enable); |
| return -EINVAL; |
| } |
| err = inv_gyro_read_bootsampl(st, enable); |
| if (err) |
| return err; |
| st->read_gyro_boot_sample = enable; |
| return count; |
| |
| } |
| |
| static ssize_t read_acc_boot_sample_show(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct inv_mpu_state *st = iio_priv(indio_dev); |
| |
| return snprintf(buf, MAX_WR_SZ, "%d\n", |
| st->read_acc_boot_sample); |
| } |
| static ssize_t read_acc_boot_sample_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| int err; |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct inv_mpu_state *st = iio_priv(indio_dev); |
| |
| unsigned long enable = 0; |
| |
| err = kstrtoul(buf, 10, &enable); |
| if (err) |
| return err; |
| if (enable > 1) { |
| dev_err(st->dev, |
| "Invalid value of input, input=%ld\n", enable); |
| return -EINVAL; |
| } |
| err = inv_acc_read_bootsampl(st, enable); |
| if (err) |
| return err; |
| st->read_acc_boot_sample = enable; |
| return count; |
| } |
| #endif |
| |
| static const struct iio_chan_spec inv_mpu_channels[] = { |
| IIO_CHAN_SOFT_TIMESTAMP(INV_MPU_SCAN_TIMESTAMP), |
| }; |
| |
| /* special run time sysfs entry, read only */ |
| static DEVICE_ATTR(debug_reg_dump, S_IRUGO | S_IWUSR, inv_reg_dump_show, NULL); |
| static DEVICE_ATTR(out_temperature, S_IRUGO | S_IWUSR, |
| inv_temperature_show, NULL); |
| static DEVICE_ATTR(misc_self_test, S_IRUGO | S_IWUSR, inv_self_test, NULL); |
| #ifdef CONFIG_ENABLE_IAM_ACC_GYRO_BUFFERING |
| static IIO_DEVICE_ATTR(read_acc_boot_sample, 0644, |
| read_acc_boot_sample_show, read_acc_boot_sample_store, SENSOR_L_ACCEL); |
| static IIO_DEVICE_ATTR(read_gyro_boot_sample, 0644, |
| read_gyro_boot_sample_show, read_gyro_boot_sample_store, SENSOR_L_GYRO); |
| #endif |
| |
| static IIO_DEVICE_ATTR(info_anglvel_matrix, S_IRUGO, inv_attr_show, NULL, |
| ATTR_GYRO_MATRIX); |
| static IIO_DEVICE_ATTR(info_accel_matrix, S_IRUGO, inv_attr_show, NULL, |
| ATTR_ACCEL_MATRIX); |
| |
| static IIO_DEVICE_ATTR(info_gyro_sf, S_IRUGO, inv_attr_show, NULL, |
| ATTR_GYRO_SF); |
| /* write only sysfs */ |
| static DEVICE_ATTR(misc_flush_batch, S_IWUSR, NULL, inv_flush_batch_store); |
| |
| /* sensor on/off sysfs control */ |
| static IIO_DEVICE_ATTR(in_accel_enable, S_IRUGO | S_IWUSR, |
| inv_sensor_on_show, inv_sensor_on_store, SENSOR_L_ACCEL); |
| static IIO_DEVICE_ATTR(in_anglvel_enable, S_IRUGO | S_IWUSR, |
| inv_sensor_on_show, inv_sensor_on_store, SENSOR_L_GYRO); |
| #ifndef SUPPORT_ONLY_BASIC_FEATURES |
| static IIO_DEVICE_ATTR(in_eis_enable, S_IRUGO | S_IWUSR, |
| inv_sensor_on_show, inv_sensor_on_store, |
| SENSOR_L_EIS_GYRO); |
| #endif |
| static IIO_DEVICE_ATTR(in_accel_wake_enable, S_IRUGO | S_IWUSR, |
| inv_sensor_on_show, inv_sensor_on_store, |
| SENSOR_L_ACCEL_WAKE); |
| static IIO_DEVICE_ATTR(in_anglvel_wake_enable, S_IRUGO | S_IWUSR, |
| inv_sensor_on_show, inv_sensor_on_store, |
| SENSOR_L_GYRO_WAKE); |
| |
| /* sensor rate sysfs control */ |
| static IIO_DEVICE_ATTR(in_accel_rate, S_IRUGO | S_IWUSR, |
| inv_sensor_rate_show, inv_sensor_rate_store, |
| SENSOR_L_ACCEL); |
| static IIO_DEVICE_ATTR(in_anglvel_rate, S_IRUGO | S_IWUSR, inv_sensor_rate_show, |
| inv_sensor_rate_store, SENSOR_L_GYRO); |
| #ifndef SUPPORT_ONLY_BASIC_FEATURES |
| static IIO_DEVICE_ATTR(in_eis_rate, S_IRUGO | S_IWUSR, |
| inv_sensor_rate_show, inv_sensor_rate_store, |
| SENSOR_L_EIS_GYRO); |
| #endif |
| static IIO_DEVICE_ATTR(in_accel_wake_rate, S_IRUGO | S_IWUSR, |
| inv_sensor_rate_show, inv_sensor_rate_store, |
| SENSOR_L_ACCEL_WAKE); |
| static IIO_DEVICE_ATTR(in_anglvel_wake_rate, S_IRUGO | S_IWUSR, |
| inv_sensor_rate_show, inv_sensor_rate_store, |
| SENSOR_L_GYRO_WAKE); |
| |
| static IIO_DEVICE_ATTR(misc_batchmode_timeout, S_IRUGO | S_IWUSR, |
| inv_attr_show, inv_basic_attr_store, |
| ATTR_DMP_BATCHMODE_TIMEOUT); |
| |
| /* engine scale */ |
| static IIO_DEVICE_ATTR(in_accel_scale, S_IRUGO | S_IWUSR, inv_attr_show, |
| inv_misc_attr_store, ATTR_ACCEL_SCALE); |
| static IIO_DEVICE_ATTR(in_anglvel_scale, S_IRUGO | S_IWUSR, inv_attr_show, |
| inv_misc_attr_store, ATTR_GYRO_SCALE); |
| |
| |
| #ifndef SUPPORT_ONLY_BASIC_FEATURES |
| static IIO_DEVICE_ATTR(debug_lp_en_off, S_IRUGO | S_IWUSR, inv_attr_show, |
| inv_debug_store, ATTR_DMP_LP_EN_OFF); |
| static IIO_DEVICE_ATTR(debug_clock_sel, S_IRUGO | S_IWUSR, inv_attr_show, |
| inv_debug_store, ATTR_DMP_CLK_SEL); |
| static IIO_DEVICE_ATTR(debug_reg_write, S_IRUGO | S_IWUSR, inv_attr_show, |
| inv_debug_store, ATTR_DEBUG_REG_WRITE); |
| static IIO_DEVICE_ATTR(debug_reg_write_addr, S_IRUGO | S_IWUSR, inv_attr_show, |
| inv_debug_store, ATTR_DEBUG_REG_ADDR); |
| #endif |
| |
| static IIO_DEVICE_ATTR(in_accel_x_st_calibbias, S_IRUGO | S_IWUSR, |
| inv_attr_show, NULL, ATTR_ACCEL_X_ST_CALIBBIAS); |
| static IIO_DEVICE_ATTR(in_accel_y_st_calibbias, S_IRUGO | S_IWUSR, |
| inv_attr_show, NULL, ATTR_ACCEL_Y_ST_CALIBBIAS); |
| static IIO_DEVICE_ATTR(in_accel_z_st_calibbias, S_IRUGO | S_IWUSR, |
| inv_attr_show, NULL, ATTR_ACCEL_Z_ST_CALIBBIAS); |
| |
| static IIO_DEVICE_ATTR(in_anglvel_x_st_calibbias, S_IRUGO | S_IWUSR, |
| inv_attr_show, NULL, ATTR_ANGLVEL_X_ST_CALIBBIAS); |
| static IIO_DEVICE_ATTR(in_anglvel_y_st_calibbias, S_IRUGO | S_IWUSR, |
| inv_attr_show, NULL, ATTR_ANGLVEL_Y_ST_CALIBBIAS); |
| static IIO_DEVICE_ATTR(in_anglvel_z_st_calibbias, S_IRUGO | S_IWUSR, |
| inv_attr_show, NULL, ATTR_ANGLVEL_Z_ST_CALIBBIAS); |
| |
| static IIO_DEVICE_ATTR(in_accel_x_offset, S_IRUGO | S_IWUSR, |
| inv_attr_show, inv_bias_store, ATTR_ACCEL_X_OFFSET); |
| static IIO_DEVICE_ATTR(in_accel_y_offset, S_IRUGO | S_IWUSR, |
| inv_attr_show, inv_bias_store, ATTR_ACCEL_Y_OFFSET); |
| static IIO_DEVICE_ATTR(in_accel_z_offset, S_IRUGO | S_IWUSR, |
| inv_attr_show, inv_bias_store, ATTR_ACCEL_Z_OFFSET); |
| |
| static IIO_DEVICE_ATTR(in_anglvel_x_offset, S_IRUGO | S_IWUSR, |
| inv_attr_show, inv_bias_store, ATTR_GYRO_X_OFFSET); |
| static IIO_DEVICE_ATTR(in_anglvel_y_offset, S_IRUGO | S_IWUSR, |
| inv_attr_show, inv_bias_store, ATTR_GYRO_Y_OFFSET); |
| static IIO_DEVICE_ATTR(in_anglvel_z_offset, S_IRUGO | S_IWUSR, |
| inv_attr_show, inv_bias_store, ATTR_GYRO_Z_OFFSET); |
| |
| #ifndef SUPPORT_ONLY_BASIC_FEATURES |
| static IIO_DEVICE_ATTR(in_step_detector_enable, S_IRUGO | S_IWUSR, |
| inv_attr_show, inv_basic_attr_store, |
| ATTR_DMP_STEP_DETECTOR_ON); |
| static IIO_DEVICE_ATTR(in_step_detector_wake_enable, S_IRUGO | S_IWUSR, |
| inv_attr_show, inv_basic_attr_store, |
| ATTR_DMP_STEP_DETECTOR_WAKE_ON); |
| static IIO_DEVICE_ATTR(in_step_counter_enable, S_IRUGO | S_IWUSR, inv_attr_show, |
| inv_basic_attr_store, ATTR_DMP_STEP_COUNTER_ON); |
| static IIO_DEVICE_ATTR(in_step_counter_wake_enable, S_IRUGO | S_IWUSR, |
| inv_attr_show, inv_basic_attr_store, |
| ATTR_DMP_STEP_COUNTER_WAKE_ON); |
| |
| static IIO_DEVICE_ATTR(event_tilt_enable, S_IRUGO | S_IWUSR, |
| inv_attr_show, inv_basic_attr_store, |
| ATTR_DMP_TILT_ENABLE); |
| |
| static IIO_DEVICE_ATTR(event_eis_enable, S_IRUGO | S_IWUSR, |
| inv_attr_show, inv_basic_attr_store, |
| ATTR_DMP_EIS_ENABLE); |
| |
| static IIO_DEVICE_ATTR(event_pick_up_enable, S_IRUGO | S_IWUSR, |
| inv_attr_show, inv_basic_attr_store, |
| ATTR_DMP_PICK_UP_ENABLE); |
| |
| static IIO_DEVICE_ATTR(in_power_on, S_IRUGO | S_IWUSR, |
| inv_attr_show, inv_basic_attr_store, |
| ATTR_IN_POWER_ON); |
| #endif |
| |
| static const struct attribute *inv_raw_attributes[] = { |
| &dev_attr_debug_reg_dump.attr, |
| &dev_attr_out_temperature.attr, |
| &dev_attr_misc_flush_batch.attr, |
| &dev_attr_misc_self_test.attr, |
| #ifndef SUPPORT_ONLY_BASIC_FEATURES |
| &iio_dev_attr_in_power_on.dev_attr.attr, |
| #endif |
| #ifdef CONFIG_ENABLE_IAM_ACC_GYRO_BUFFERING |
| &iio_dev_attr_read_acc_boot_sample.dev_attr.attr, |
| &iio_dev_attr_read_gyro_boot_sample.dev_attr.attr, |
| #endif |
| &iio_dev_attr_in_accel_enable.dev_attr.attr, |
| &iio_dev_attr_in_accel_wake_enable.dev_attr.attr, |
| &iio_dev_attr_info_accel_matrix.dev_attr.attr, |
| &iio_dev_attr_in_accel_scale.dev_attr.attr, |
| &iio_dev_attr_misc_batchmode_timeout.dev_attr.attr, |
| &iio_dev_attr_in_accel_rate.dev_attr.attr, |
| &iio_dev_attr_in_accel_wake_rate.dev_attr.attr, |
| }; |
| |
| #ifndef SUPPORT_ONLY_BASIC_FEATURES |
| static const struct attribute *inv_debug_attributes[] = { |
| &iio_dev_attr_debug_lp_en_off.dev_attr.attr, |
| &iio_dev_attr_debug_clock_sel.dev_attr.attr, |
| &iio_dev_attr_debug_reg_write.dev_attr.attr, |
| &iio_dev_attr_debug_reg_write_addr.dev_attr.attr, |
| }; |
| #endif |
| |
| static const struct attribute *inv_gyro_attributes[] = { |
| &iio_dev_attr_info_anglvel_matrix.dev_attr.attr, |
| &iio_dev_attr_in_anglvel_enable.dev_attr.attr, |
| &iio_dev_attr_in_anglvel_rate.dev_attr.attr, |
| #ifndef SUPPORT_ONLY_BASIC_FEATURES |
| &iio_dev_attr_in_eis_enable.dev_attr.attr, |
| #endif |
| &iio_dev_attr_in_anglvel_wake_enable.dev_attr.attr, |
| &iio_dev_attr_in_anglvel_scale.dev_attr.attr, |
| #ifndef SUPPORT_ONLY_BASIC_FEATURES |
| &iio_dev_attr_in_eis_rate.dev_attr.attr, |
| #endif |
| &iio_dev_attr_in_anglvel_wake_rate.dev_attr.attr, |
| &iio_dev_attr_info_gyro_sf.dev_attr.attr, |
| }; |
| |
| static const struct attribute *inv_bias_attributes[] = { |
| &iio_dev_attr_in_accel_x_st_calibbias.dev_attr.attr, |
| &iio_dev_attr_in_accel_y_st_calibbias.dev_attr.attr, |
| &iio_dev_attr_in_accel_z_st_calibbias.dev_attr.attr, |
| &iio_dev_attr_in_accel_x_offset.dev_attr.attr, |
| &iio_dev_attr_in_accel_y_offset.dev_attr.attr, |
| &iio_dev_attr_in_accel_z_offset.dev_attr.attr, |
| &iio_dev_attr_in_anglvel_x_st_calibbias.dev_attr.attr, |
| &iio_dev_attr_in_anglvel_y_st_calibbias.dev_attr.attr, |
| &iio_dev_attr_in_anglvel_z_st_calibbias.dev_attr.attr, |
| &iio_dev_attr_in_anglvel_x_offset.dev_attr.attr, |
| &iio_dev_attr_in_anglvel_y_offset.dev_attr.attr, |
| &iio_dev_attr_in_anglvel_z_offset.dev_attr.attr, |
| }; |
| |
| #ifndef SUPPORT_ONLY_BASIC_FEATURES |
| static const struct attribute *inv_pedometer_attributes[] = { |
| &iio_dev_attr_event_tilt_enable.dev_attr.attr, |
| &iio_dev_attr_event_eis_enable.dev_attr.attr, |
| &iio_dev_attr_event_pick_up_enable.dev_attr.attr, |
| &iio_dev_attr_in_step_counter_enable.dev_attr.attr, |
| &iio_dev_attr_in_step_counter_wake_enable.dev_attr.attr, |
| &iio_dev_attr_in_step_detector_enable.dev_attr.attr, |
| &iio_dev_attr_in_step_detector_wake_enable.dev_attr.attr, |
| }; |
| #endif |
| |
| static struct attribute *inv_attributes[ARRAY_SIZE(inv_raw_attributes) + |
| #ifndef SUPPORT_ONLY_BASIC_FEATURES |
| ARRAY_SIZE(inv_debug_attributes) + |
| #endif |
| ARRAY_SIZE(inv_gyro_attributes) + |
| ARRAY_SIZE(inv_bias_attributes) + |
| #ifndef SUPPORT_ONLY_BASIC_FEATURES |
| ARRAY_SIZE(inv_pedometer_attributes) + |
| #endif |
| + 1]; |
| |
| static const struct attribute_group inv_attribute_group = { |
| .name = "mpu", |
| .attrs = inv_attributes |
| }; |
| |
| static const struct iio_info mpu_info = { |
| .driver_module = THIS_MODULE, |
| .attrs = &inv_attribute_group, |
| }; |
| |
| /* |
| * inv_check_chip_type() - check and setup chip type. |
| */ |
| int inv_check_chip_type(struct iio_dev *indio_dev, const char *name) |
| { |
| int result; |
| int t_ind; |
| struct inv_chip_config_s *conf; |
| struct mpu_platform_data *plat; |
| struct inv_mpu_state *st; |
| |
| st = iio_priv(indio_dev); |
| conf = &st->chip_config; |
| plat = &st->plat_data; |
| |
| if (!strcmp(name, "iam20680")) |
| st->chip_type = IAM20680; |
| else |
| return -EPERM; |
| st->chip_config.has_gyro = 1; |
| |
| st->hw = &hw_info[st->chip_type]; |
| result = inv_mpu_initialize(st); |
| if (result) |
| return result; |
| |
| t_ind = 0; |
| memcpy(&inv_attributes[t_ind], inv_raw_attributes, |
| sizeof(inv_raw_attributes)); |
| t_ind += ARRAY_SIZE(inv_raw_attributes); |
| |
| #ifndef SUPPORT_ONLY_BASIC_FEATURES |
| memcpy(&inv_attributes[t_ind], inv_pedometer_attributes, |
| sizeof(inv_pedometer_attributes)); |
| t_ind += ARRAY_SIZE(inv_pedometer_attributes); |
| #endif |
| |
| memcpy(&inv_attributes[t_ind], inv_gyro_attributes, |
| sizeof(inv_gyro_attributes)); |
| t_ind += ARRAY_SIZE(inv_gyro_attributes); |
| |
| memcpy(&inv_attributes[t_ind], inv_bias_attributes, |
| sizeof(inv_bias_attributes)); |
| t_ind += ARRAY_SIZE(inv_bias_attributes); |
| |
| #ifndef SUPPORT_ONLY_BASIC_FEATURES |
| memcpy(&inv_attributes[t_ind], inv_debug_attributes, |
| sizeof(inv_debug_attributes)); |
| t_ind += ARRAY_SIZE(inv_debug_attributes); |
| #endif |
| |
| inv_attributes[t_ind] = NULL; |
| |
| indio_dev->channels = inv_mpu_channels; |
| indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels); |
| |
| indio_dev->info = &mpu_info; |
| indio_dev->modes = INDIO_DIRECT_MODE; |
| indio_dev->currentmode = INDIO_DIRECT_MODE; |
| |
| return result; |
| } |
| EXPORT_SYMBOL_GPL(inv_check_chip_type); |
| |
| int inv_create_dmp_sysfs(struct iio_dev *ind) |
| { |
| // dummy |
| return 0; |
| } |
| EXPORT_SYMBOL_GPL(inv_create_dmp_sysfs); |
| |
| MODULE_AUTHOR("Invensense Corporation"); |
| MODULE_DESCRIPTION("Invensense device ICM20xxx driver"); |
| MODULE_LICENSE("GPL"); |