/* * three 16-bit accelerometer output registers for X/Y/Z axis * we use only XOUT_L as a base register, all other addresses * can be obtained by applying an offset and are provided here * only for clarity.
*/ #define KMX61_ACC_XOUT_L 0x0A #define KMX61_ACC_XOUT_H 0x0B #define KMX61_ACC_YOUT_L 0x0C #define KMX61_ACC_YOUT_H 0x0D #define KMX61_ACC_ZOUT_L 0x0E #define KMX61_ACC_ZOUT_H 0x0F
/* * one 16-bit temperature output register
*/ #define KMX61_TEMP_L 0x10 #define KMX61_TEMP_H 0x11
staticint kmx61_convert_freq_to_bit(int val, int val2)
{ int i;
for (i = 0; i < ARRAY_SIZE(kmx61_samp_freq_table); i++) if (val == kmx61_samp_freq_table[i].val &&
val2 == kmx61_samp_freq_table[i].val2) return i; return -EINVAL;
}
staticint kmx61_convert_wake_up_odr_to_bit(int val, int val2)
{ int i;
for (i = 0; i < ARRAY_SIZE(kmx61_wake_up_odr_table); ++i) if (kmx61_wake_up_odr_table[i].val == val &&
kmx61_wake_up_odr_table[i].val2 == val2) return kmx61_wake_up_odr_table[i].odr_bits; return -EINVAL;
}
/** * kmx61_set_mode() - set KMX61 device operating mode * @data: kmx61 device private data pointer * @mode: bitmask, indicating operating mode for @device * @device: bitmask, indicating device for which @mode needs to be set * @update: update stby bits stored in device's private @data * * For each sensor (accelerometer/magnetometer) there are two operating modes * STANDBY and OPERATION. Neither accel nor magn can be disabled independently * if they are both enabled. Internal sensors state is saved in acc_stby and * mag_stby members of driver's private @data.
*/ staticint kmx61_set_mode(struct kmx61_data *data, u8 mode, u8 device, bool update)
{ int ret; int acc_stby = -1, mag_stby = -1;
ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_STBY); if (ret < 0) {
dev_err(&data->client->dev, "Error reading reg_stby\n"); return ret;
} if (device & KMX61_ACC) { if (mode & KMX61_ACC_STBY_BIT) {
ret |= KMX61_ACC_STBY_BIT;
acc_stby = 1;
} else {
ret &= ~KMX61_ACC_STBY_BIT;
acc_stby = 0;
}
}
if (device & KMX61_MAG) { if (mode & KMX61_MAG_STBY_BIT) {
ret |= KMX61_MAG_STBY_BIT;
mag_stby = 1;
} else {
ret &= ~KMX61_MAG_STBY_BIT;
mag_stby = 0;
}
}
if (mode & KMX61_ACT_STBY_BIT)
ret |= KMX61_ACT_STBY_BIT;
ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_STBY, ret); if (ret < 0) {
dev_err(&data->client->dev, "Error writing reg_stby\n"); return ret;
}
if (acc_stby != -1 && update)
data->acc_stby = acc_stby; if (mag_stby != -1 && update)
data->mag_stby = mag_stby;
for (i = 0; i < ARRAY_SIZE(kmx61_uscale_table); i++) { if (kmx61_uscale_table[i] == uscale) {
ret = kmx61_get_mode(data, &mode,
KMX61_ACC | KMX61_MAG); if (ret < 0) return ret;
ret = kmx61_set_mode(data, KMX61_ALL_STBY,
KMX61_ACC | KMX61_MAG, true); if (ret < 0) return ret;
ret = kmx61_set_range(data, i); if (ret < 0) return ret;
/* set accel 12bit, 4g range */
ret = kmx61_set_range(data, KMX61_RANGE_4G); if (ret < 0) return ret;
ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_ODCNTL); if (ret < 0) {
dev_err(&data->client->dev, "Error reading reg_odcntl\n"); return ret;
}
data->odr_bits = ret;
/* * set output data rate for wake up (motion detection) function * to match data rate for accelerometer sampling
*/
ret = kmx61_get_odr(data, &val, &val2, KMX61_ACC); if (ret < 0) return ret;
ret = kmx61_set_wake_up_odr(data, val, val2); if (ret < 0) return ret;
/* set acc/magn to OPERATION mode */
ret = kmx61_set_mode(data, 0, KMX61_ACC | KMX61_MAG, true); if (ret < 0) return ret;
ret = kmx61_get_mode(data, &mode, KMX61_ACC | KMX61_MAG); if (ret < 0) return ret;
ret = kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, true); if (ret < 0) return ret;
ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_INC1); if (ret < 0) {
dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); return ret;
}
if (status) {
ret |= KMX61_REG_INC1_BIT_IEN; if (device & KMX61_ACC)
ret |= KMX61_REG_INC1_BIT_DRDYA; if (device & KMX61_MAG)
ret |= KMX61_REG_INC1_BIT_DRDYM;
} else {
ret &= ~KMX61_REG_INC1_BIT_IEN; if (device & KMX61_ACC)
ret &= ~KMX61_REG_INC1_BIT_DRDYA; if (device & KMX61_MAG)
ret &= ~KMX61_REG_INC1_BIT_DRDYM;
}
ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_INC1, ret); if (ret < 0) {
dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n"); return ret;
}
ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_CTRL1); if (ret < 0) {
dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); return ret;
}
if (status)
ret |= KMX61_REG_CTRL1_BIT_DRDYE; else
ret &= ~KMX61_REG_CTRL1_BIT_DRDYE;
ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_CTRL1, ret); if (ret < 0) {
dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); return ret;
}
/** * kmx61_set_power_state() - set power state for kmx61 @device * @data: kmx61 device private pointer * @on: power state to be set for @device * @device: bitmask indicating device for which @on state needs to be set * * Notice that when ACC power state needs to be set to ON and MAG is in * OPERATION then we know that kmx61_runtime_resume was already called * so we must set ACC OPERATION mode here. The same happens when MAG power * state needs to be set to ON and ACC is in OPERATION.
*/ staticint kmx61_set_power_state(struct kmx61_data *data, bool on, u8 device)
{ #ifdef CONFIG_PM int ret;
if (device & KMX61_ACC) { if (on && !data->acc_ps && !data->mag_stby) {
ret = kmx61_set_mode(data, 0, KMX61_ACC, true); if (ret < 0) return ret;
}
data->acc_ps = on;
} if (device & KMX61_MAG) { if (on && !data->mag_ps && !data->acc_stby) {
ret = kmx61_set_mode(data, 0, KMX61_MAG, true); if (ret < 0) return ret;
}
data->mag_ps = on;
}
if (on) {
ret = pm_runtime_resume_and_get(&data->client->dev);
} else {
pm_runtime_mark_last_busy(&data->client->dev);
ret = pm_runtime_put_autosuspend(&data->client->dev);
} if (ret < 0) {
dev_err(&data->client->dev, "Failed: kmx61_set_power_state for %d, ret %d\n",
on, ret);
return ret;
} #endif return 0;
}
staticint kmx61_read_measurement(struct kmx61_data *data, u8 base, u8 offset)
{ int ret;
u8 reg = base + offset * 2;
ret = i2c_smbus_read_word_data(data->client, reg); if (ret < 0)
dev_err(&data->client->dev, "failed to read reg at %x\n", reg);
return ret;
}
staticint kmx61_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask)
{ int ret;
u8 base_reg; struct kmx61_data *data = kmx61_get_data(indio_dev);
switch (mask) { case IIO_CHAN_INFO_RAW: switch (chan->type) { case IIO_ACCEL:
base_reg = KMX61_ACC_XOUT_L; break; case IIO_MAGN:
base_reg = KMX61_MAG_XOUT_L; break; default: return -EINVAL;
}
mutex_lock(&data->lock);
ret = kmx61_set_power_state(data, true, chan->address); if (ret) {
mutex_unlock(&data->lock); return ret;
}
ret = kmx61_read_measurement(data, base_reg, chan->scan_index); if (ret < 0) {
kmx61_set_power_state(data, false, chan->address);
mutex_unlock(&data->lock); return ret;
}
*val = sign_extend32(ret >> chan->scan_type.shift,
chan->scan_type.realbits - 1);
ret = kmx61_set_power_state(data, false, chan->address);
mutex_unlock(&data->lock); if (ret) return ret; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_ACCEL:
*val = 0;
*val2 = kmx61_uscale_table[data->range]; return IIO_VAL_INT_PLUS_MICRO; case IIO_MAGN: /* 14 bits res, 1465 microGauss per magn count */
*val = 0;
*val2 = 1465; return IIO_VAL_INT_PLUS_MICRO; default: return -EINVAL;
} case IIO_CHAN_INFO_SAMP_FREQ: if (chan->type != IIO_ACCEL && chan->type != IIO_MAGN) return -EINVAL;
mutex_lock(&data->lock);
ret = kmx61_get_odr(data, val, val2, chan->address);
mutex_unlock(&data->lock); if (ret) return -EINVAL; return IIO_VAL_INT_PLUS_MICRO;
} return -EINVAL;
}
staticint kmx61_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask)
{ int ret; struct kmx61_data *data = kmx61_get_data(indio_dev);
switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: if (chan->type != IIO_ACCEL && chan->type != IIO_MAGN) return -EINVAL;
mutex_lock(&data->lock);
ret = kmx61_set_odr(data, val, val2, chan->address);
mutex_unlock(&data->lock); return ret; case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_ACCEL: if (val != 0) return -EINVAL;
mutex_lock(&data->lock);
ret = kmx61_set_scale(data, val2);
mutex_unlock(&data->lock); return ret; default: return -EINVAL;
} default: return -EINVAL;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.