diff options
Diffstat (limited to 'drivers/iio/accel/mma8452.c')
-rw-r--r-- | drivers/iio/accel/mma8452.c | 223 |
1 files changed, 177 insertions, 46 deletions
diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index e225d3c53bd5..d41e1b588e68 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -1,22 +1,22 @@ /* - * mma8452.c - Support for following Freescale 3-axis accelerometers: + * mma8452.c - Support for following Freescale / NXP 3-axis accelerometers: * - * MMA8451Q (14 bit) - * MMA8452Q (12 bit) - * MMA8453Q (10 bit) - * MMA8652FC (12 bit) - * MMA8653FC (10 bit) - * FXLS8471Q (14 bit) + * device name digital output 7-bit I2C slave address (pin selectable) + * --------------------------------------------------------------------- + * MMA8451Q 14 bit 0x1c / 0x1d + * MMA8452Q 12 bit 0x1c / 0x1d + * MMA8453Q 10 bit 0x1c / 0x1d + * MMA8652FC 12 bit 0x1d + * MMA8653FC 10 bit 0x1d + * FXLS8471Q 14 bit 0x1e / 0x1d / 0x1c / 0x1f * - * Copyright 2015 Martin Kepplinger <martin.kepplinger@theobroma-systems.com> + * Copyright 2015 Martin Kepplinger <martink@posteo.de> * Copyright 2014 Peter Meerwald <pmeerw@pmeerw.net> * * This file is subject to the terms and conditions of version 2 of * the GNU General Public License. See the file COPYING in the main * directory of this archive for more details. * - * 7-bit I2C slave address 0x1c/0x1d (pin selectable) - * * TODO: orientation events */ @@ -76,6 +76,8 @@ #define MMA8452_CTRL_DR_DEFAULT 0x4 /* 50 Hz sample frequency */ #define MMA8452_CTRL_REG2 0x2b #define MMA8452_CTRL_REG2_RST BIT(6) +#define MMA8452_CTRL_REG2_MODS_SHIFT 3 +#define MMA8452_CTRL_REG2_MODS_MASK 0x1b #define MMA8452_CTRL_REG4 0x2d #define MMA8452_CTRL_REG5 0x2e #define MMA8452_OFF_X 0x2f @@ -106,7 +108,7 @@ struct mma8452_data { }; /** - * struct mma_chip_info - chip specific data for Freescale's accelerometers + * struct mma_chip_info - chip specific data * @chip_id: WHO_AM_I register's value * @channels: struct iio_chan_spec matching the device's * capabilities @@ -257,20 +259,17 @@ static const int mma8452_samp_freq[8][2] = { {6, 250000}, {1, 560000} }; -/* Datasheet table 35 (step time vs sample frequency) */ -static const int mma8452_transient_time_step_us[8] = { - 1250, - 2500, - 5000, - 10000, - 20000, - 20000, - 20000, - 20000 +/* Datasheet table: step time "Relationship with the ODR" (sample frequency) */ +static const int mma8452_transient_time_step_us[4][8] = { + { 1250, 2500, 5000, 10000, 20000, 20000, 20000, 20000 }, /* normal */ + { 1250, 2500, 5000, 10000, 20000, 80000, 80000, 80000 }, /* l p l n */ + { 1250, 2500, 2500, 2500, 2500, 2500, 2500, 2500 }, /* high res*/ + { 1250, 2500, 5000, 10000, 20000, 80000, 160000, 160000 } /* l p */ }; -/* Datasheet table 18 (normal mode) */ -static const int mma8452_hp_filter_cutoff[8][4][2] = { +/* Datasheet table "High-Pass Filter Cutoff Options" */ +static const int mma8452_hp_filter_cutoff[4][8][4][2] = { + { /* normal */ { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 800 Hz sample */ { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 400 Hz sample */ { {8, 0}, {4, 0}, {2, 0}, {1, 0} }, /* 200 Hz sample */ @@ -279,8 +278,61 @@ static const int mma8452_hp_filter_cutoff[8][4][2] = { { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 12.5 Hz sample */ { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 6.25 Hz sample */ { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} } /* 1.56 Hz sample */ + }, + { /* low noise low power */ + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {8, 0}, {4, 0}, {2, 0}, {1, 0} }, + { {4, 0}, {2, 0}, {1, 0}, {0, 500000} }, + { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, + { {0, 500000}, {0, 250000}, {0, 125000}, {0, 063000} }, + { {0, 500000}, {0, 250000}, {0, 125000}, {0, 063000} }, + { {0, 500000}, {0, 250000}, {0, 125000}, {0, 063000} } + }, + { /* high resolution */ + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} } + }, + { /* low power */ + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {8, 0}, {4, 0}, {2, 0}, {1, 0} }, + { {4, 0}, {2, 0}, {1, 0}, {0, 500000} }, + { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, + { {1, 0}, {0, 500000}, {0, 250000}, {0, 125000} }, + { {0, 250000}, {0, 125000}, {0, 063000}, {0, 031000} }, + { {0, 250000}, {0, 125000}, {0, 063000}, {0, 031000} }, + { {0, 250000}, {0, 125000}, {0, 063000}, {0, 031000} } + } }; +/* Datasheet table "MODS Oversampling modes averaging values at each ODR" */ +static const u16 mma8452_os_ratio[4][8] = { + /* 800 Hz, 400 Hz, ... , 1.56 Hz */ + { 2, 4, 4, 4, 4, 16, 32, 128 }, /* normal */ + { 2, 4, 4, 4, 4, 4, 8, 32 }, /* low power low noise */ + { 2, 4, 8, 16, 32, 128, 256, 1024 }, /* high resolution */ + { 2, 2, 2, 2, 2, 2, 4, 16 } /* low power */ +}; + +static int mma8452_get_power_mode(struct mma8452_data *data) +{ + int reg; + + reg = i2c_smbus_read_byte_data(data->client, + MMA8452_CTRL_REG2); + if (reg < 0) + return reg; + + return ((reg & MMA8452_CTRL_REG2_MODS_MASK) >> + MMA8452_CTRL_REG2_MODS_SHIFT); +} + static ssize_t mma8452_show_samp_freq_avail(struct device *dev, struct device_attribute *attr, char *buf) @@ -306,10 +358,39 @@ static ssize_t mma8452_show_hp_cutoff_avail(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct mma8452_data *data = iio_priv(indio_dev); + int i, j; + + i = mma8452_get_odr_index(data); + j = mma8452_get_power_mode(data); + if (j < 0) + return j; + + return mma8452_show_int_plus_micros(buf, mma8452_hp_filter_cutoff[j][i], + ARRAY_SIZE(mma8452_hp_filter_cutoff[0][0])); +} + +static ssize_t mma8452_show_os_ratio_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct mma8452_data *data = iio_priv(indio_dev); int i = mma8452_get_odr_index(data); + int j; + u16 val = 0; + size_t len = 0; - return mma8452_show_int_plus_micros(buf, mma8452_hp_filter_cutoff[i], - ARRAY_SIZE(mma8452_hp_filter_cutoff[0])); + for (j = 0; j < ARRAY_SIZE(mma8452_os_ratio); j++) { + if (val == mma8452_os_ratio[j][i]) + continue; + + val = mma8452_os_ratio[j][i]; + + len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", val); + } + buf[len - 1] = '\n'; + + return len; } static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mma8452_show_samp_freq_avail); @@ -317,6 +398,8 @@ static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO, mma8452_show_scale_avail, NULL, 0); static IIO_DEVICE_ATTR(in_accel_filter_high_pass_3db_frequency_available, S_IRUGO, mma8452_show_hp_cutoff_avail, NULL, 0); +static IIO_DEVICE_ATTR(in_accel_oversampling_ratio_available, S_IRUGO, + mma8452_show_os_ratio_avail, NULL, 0); static int mma8452_get_samp_freq_index(struct mma8452_data *data, int val, int val2) @@ -335,24 +418,33 @@ static int mma8452_get_scale_index(struct mma8452_data *data, int val, int val2) static int mma8452_get_hp_filter_index(struct mma8452_data *data, int val, int val2) { - int i = mma8452_get_odr_index(data); + int i, j; + + i = mma8452_get_odr_index(data); + j = mma8452_get_power_mode(data); + if (j < 0) + return j; - return mma8452_get_int_plus_micros_index(mma8452_hp_filter_cutoff[i], - ARRAY_SIZE(mma8452_hp_filter_cutoff[0]), val, val2); + return mma8452_get_int_plus_micros_index(mma8452_hp_filter_cutoff[j][i], + ARRAY_SIZE(mma8452_hp_filter_cutoff[0][0]), val, val2); } static int mma8452_read_hp_filter(struct mma8452_data *data, int *hz, int *uHz) { - int i, ret; + int j, i, ret; ret = i2c_smbus_read_byte_data(data->client, MMA8452_HP_FILTER_CUTOFF); if (ret < 0) return ret; i = mma8452_get_odr_index(data); + j = mma8452_get_power_mode(data); + if (j < 0) + return j; + ret &= MMA8452_HP_FILTER_CUTOFF_SEL_MASK; - *hz = mma8452_hp_filter_cutoff[i][ret][0]; - *uHz = mma8452_hp_filter_cutoff[i][ret][1]; + *hz = mma8452_hp_filter_cutoff[j][i][ret][0]; + *uHz = mma8452_hp_filter_cutoff[j][i][ret][1]; return 0; } @@ -414,6 +506,15 @@ static int mma8452_read_raw(struct iio_dev *indio_dev, } return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + ret = mma8452_get_power_mode(data); + if (ret < 0) + return ret; + + i = mma8452_get_odr_index(data); + + *val = mma8452_os_ratio[ret][i]; + return IIO_VAL_INT; } return -EINVAL; @@ -480,6 +581,21 @@ fail: return ret; } +static int mma8452_set_power_mode(struct mma8452_data *data, u8 mode) +{ + int reg; + + reg = i2c_smbus_read_byte_data(data->client, + MMA8452_CTRL_REG2); + if (reg < 0) + return reg; + + reg &= ~MMA8452_CTRL_REG2_MODS_MASK; + reg |= mode << MMA8452_CTRL_REG2_MODS_SHIFT; + + return mma8452_change_config(data, MMA8452_CTRL_REG2, reg); +} + /* returns >0 if in freefall mode, 0 if not or <0 if an error occurred */ static int mma8452_freefall_mode_enabled(struct mma8452_data *data) { @@ -518,11 +634,7 @@ static int mma8452_set_freefall_mode(struct mma8452_data *data, bool state) val |= MMA8452_FF_MT_CFG_OAE; } - val = mma8452_change_config(data, chip->ev_cfg, val); - if (val) - return val; - - return 0; + return mma8452_change_config(data, chip->ev_cfg, val); } static int mma8452_set_hp_filter_frequency(struct mma8452_data *data, @@ -597,6 +709,14 @@ static int mma8452_write_raw(struct iio_dev *indio_dev, return mma8452_change_config(data, MMA8452_DATA_CFG, data->data_cfg); + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + ret = mma8452_get_odr_index(data); + + for (i = 0; i < ARRAY_SIZE(mma8452_os_ratio); i++) { + if (mma8452_os_ratio[i][ret] == val) + return mma8452_set_power_mode(data, i); + } + default: return -EINVAL; } @@ -610,7 +730,7 @@ static int mma8452_read_thresh(struct iio_dev *indio_dev, int *val, int *val2) { struct mma8452_data *data = iio_priv(indio_dev); - int ret, us; + int ret, us, power_mode; switch (info) { case IIO_EV_INFO_VALUE: @@ -629,7 +749,11 @@ static int mma8452_read_thresh(struct iio_dev *indio_dev, if (ret < 0) return ret; - us = ret * mma8452_transient_time_step_us[ + power_mode = mma8452_get_power_mode(data); + if (power_mode < 0) + return power_mode; + + us = ret * mma8452_transient_time_step_us[power_mode][ mma8452_get_odr_index(data)]; *val = us / USEC_PER_SEC; *val2 = us % USEC_PER_SEC; @@ -677,8 +801,12 @@ static int mma8452_write_thresh(struct iio_dev *indio_dev, val); case IIO_EV_INFO_PERIOD: + ret = mma8452_get_power_mode(data); + if (ret < 0) + return ret; + steps = (val * USEC_PER_SEC + val2) / - mma8452_transient_time_step_us[ + mma8452_transient_time_step_us[ret][ mma8452_get_odr_index(data)]; if (steps < 0 || steps > 0xff) @@ -785,7 +913,7 @@ static int mma8452_write_event_config(struct iio_dev *indio_dev, static void mma8452_transient_interrupt(struct iio_dev *indio_dev) { struct mma8452_data *data = iio_priv(indio_dev); - s64 ts = iio_get_time_ns(); + s64 ts = iio_get_time_ns(indio_dev); int src; src = i2c_smbus_read_byte_data(data->client, data->chip_info->ev_src); @@ -865,7 +993,7 @@ static irqreturn_t mma8452_trigger_handler(int irq, void *p) goto done; iio_push_to_buffers_with_timestamp(indio_dev, buffer, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); @@ -978,7 +1106,8 @@ static struct attribute_group mma8452_event_attribute_group = { BIT(IIO_CHAN_INFO_CALIBBIAS), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \ + BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .scan_index = idx, \ .scan_type = { \ .sign = 's', \ @@ -998,7 +1127,8 @@ static struct attribute_group mma8452_event_attribute_group = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_CALIBBIAS), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ - BIT(IIO_CHAN_INFO_SCALE), \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .scan_index = idx, \ .scan_type = { \ .sign = 's', \ @@ -1171,6 +1301,7 @@ static struct attribute *mma8452_attributes[] = { &iio_dev_attr_sampling_frequency_available.dev_attr.attr, &iio_dev_attr_in_accel_scale_available.dev_attr.attr, &iio_dev_attr_in_accel_filter_high_pass_3db_frequency_available.dev_attr.attr, + &iio_dev_attr_in_accel_oversampling_ratio_available.dev_attr.attr, NULL }; @@ -1444,8 +1575,8 @@ static int mma8452_probe(struct i2c_client *client, goto buffer_cleanup; ret = mma8452_set_freefall_mode(data, false); - if (ret) - return ret; + if (ret < 0) + goto buffer_cleanup; return 0; @@ -1558,5 +1689,5 @@ static struct i2c_driver mma8452_driver = { module_i2c_driver(mma8452_driver); MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); -MODULE_DESCRIPTION("Freescale MMA8452 accelerometer driver"); +MODULE_DESCRIPTION("Freescale / NXP MMA8452 accelerometer driver"); MODULE_LICENSE("GPL"); |