/* if nothing changed, exit */ if (gyro == oldgyro && accel == oldaccel && temp == oldtemp) return 0;
val = INV_ICM42600_PWR_MGMT0_GYRO(gyro) |
INV_ICM42600_PWR_MGMT0_ACCEL(accel); if (!temp)
val |= INV_ICM42600_PWR_MGMT0_TEMP_DIS;
ret = regmap_write(st->map, INV_ICM42600_REG_PWR_MGMT0, val); if (ret) return ret;
/* compute required wait time for sensors to stabilize */
sleepval = 0; /* temperature stabilization time */ if (temp && !oldtemp) { if (sleepval < INV_ICM42600_TEMP_STARTUP_TIME_MS)
sleepval = INV_ICM42600_TEMP_STARTUP_TIME_MS;
} /* accel startup time */ if (accel != oldaccel && oldaccel == INV_ICM42600_SENSOR_MODE_OFF) { /* block any register write for at least 200 µs */
usleep_range(200, 300); if (sleepval < INV_ICM42600_ACCEL_STARTUP_TIME_MS)
sleepval = INV_ICM42600_ACCEL_STARTUP_TIME_MS;
} if (gyro != oldgyro) { /* gyro startup time */ if (oldgyro == INV_ICM42600_SENSOR_MODE_OFF) { /* block any register write for at least 200 µs */
usleep_range(200, 300); if (sleepval < INV_ICM42600_GYRO_STARTUP_TIME_MS)
sleepval = INV_ICM42600_GYRO_STARTUP_TIME_MS; /* gyro stop time */
} elseif (gyro == INV_ICM42600_SENSOR_MODE_OFF) { if (sleepval < INV_ICM42600_GYRO_STOP_TIME_MS)
sleepval = INV_ICM42600_GYRO_STOP_TIME_MS;
}
}
/* deferred sleep value if sleep pointer is provided or direct sleep */ if (sleep_ms)
*sleep_ms = sleepval; elseif (sleepval)
msleep(sleepval);
return 0;
}
int inv_icm42600_set_accel_conf(struct inv_icm42600_state *st, struct inv_icm42600_sensor_conf *conf, unsignedint *sleep_ms)
{ struct inv_icm42600_sensor_conf *oldconf = &st->conf.accel; unsignedint val; int ret;
/* Sanitize missing values with current values */ if (conf->mode < 0)
conf->mode = oldconf->mode; if (conf->fs < 0)
conf->fs = oldconf->fs; if (conf->odr < 0)
conf->odr = oldconf->odr; if (conf->filter < 0)
conf->filter = oldconf->filter;
/* force power mode against ODR when sensor is on */ switch (conf->mode) { case INV_ICM42600_SENSOR_MODE_LOW_POWER: case INV_ICM42600_SENSOR_MODE_LOW_NOISE: if (conf->odr <= INV_ICM42600_ODR_1KHZ_LN) {
conf->mode = INV_ICM42600_SENSOR_MODE_LOW_NOISE;
conf->filter = INV_ICM42600_FILTER_BW_ODR_DIV_2;
} elseif (conf->odr >= INV_ICM42600_ODR_6_25HZ_LP &&
conf->odr <= INV_ICM42600_ODR_1_5625HZ_LP) {
conf->mode = INV_ICM42600_SENSOR_MODE_LOW_POWER;
conf->filter = INV_ICM42600_FILTER_AVG_16X;
} break; default: break;
}
/* set ACCEL_CONFIG0 register (accel fullscale & odr) */ if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) {
val = INV_ICM42600_ACCEL_CONFIG0_FS(conf->fs) |
INV_ICM42600_ACCEL_CONFIG0_ODR(conf->odr);
ret = regmap_write(st->map, INV_ICM42600_REG_ACCEL_CONFIG0, val); if (ret) return ret;
oldconf->fs = conf->fs;
oldconf->odr = conf->odr;
}
/* set GYRO_ACCEL_CONFIG0 register (accel filter) */ if (conf->filter != oldconf->filter) {
val = INV_ICM42600_GYRO_ACCEL_CONFIG0_ACCEL_FILT(conf->filter) |
INV_ICM42600_GYRO_ACCEL_CONFIG0_GYRO_FILT(st->conf.gyro.filter);
ret = regmap_write(st->map, INV_ICM42600_REG_GYRO_ACCEL_CONFIG0, val); if (ret) return ret;
oldconf->filter = conf->filter;
}
/* set PWR_MGMT0 register (gyro & accel sensor mode, temp enabled) */
val = INV_ICM42600_PWR_MGMT0_GYRO(conf->gyro.mode) |
INV_ICM42600_PWR_MGMT0_ACCEL(conf->accel.mode); if (!conf->temp_en)
val |= INV_ICM42600_PWR_MGMT0_TEMP_DIS;
ret = regmap_write(st->map, INV_ICM42600_REG_PWR_MGMT0, val); if (ret) return ret;
/* set GYRO_CONFIG0 register (gyro fullscale & odr) */
val = INV_ICM42600_GYRO_CONFIG0_FS(conf->gyro.fs) |
INV_ICM42600_GYRO_CONFIG0_ODR(conf->gyro.odr);
ret = regmap_write(st->map, INV_ICM42600_REG_GYRO_CONFIG0, val); if (ret) return ret;
/* set ACCEL_CONFIG0 register (accel fullscale & odr) */
val = INV_ICM42600_ACCEL_CONFIG0_FS(conf->accel.fs) |
INV_ICM42600_ACCEL_CONFIG0_ODR(conf->accel.odr);
ret = regmap_write(st->map, INV_ICM42600_REG_ACCEL_CONFIG0, val); if (ret) return ret;
/* set GYRO_ACCEL_CONFIG0 register (gyro & accel filters) */
val = INV_ICM42600_GYRO_ACCEL_CONFIG0_ACCEL_FILT(conf->accel.filter) |
INV_ICM42600_GYRO_ACCEL_CONFIG0_GYRO_FILT(conf->gyro.filter);
ret = regmap_write(st->map, INV_ICM42600_REG_GYRO_ACCEL_CONFIG0, val); if (ret) return ret;
/* update internal conf */
st->conf = *conf;
return 0;
}
/** * inv_icm42600_setup() - check and setup chip * @st: driver internal state * @bus_setup: callback for setting up bus specific registers * * Returns 0 on success, a negative error code otherwise.
*/ staticint inv_icm42600_setup(struct inv_icm42600_state *st,
inv_icm42600_bus_setup bus_setup)
{ conststruct inv_icm42600_hw *hw = &inv_icm42600_hw[st->chip]; conststruct device *dev = regmap_get_device(st->map); unsignedint val; int ret;
/* check chip self-identification value */
ret = regmap_read(st->map, INV_ICM42600_REG_WHOAMI, &val); if (ret) return ret; if (val != hw->whoami) {
dev_err(dev, "invalid whoami %#02x expected %#02x (%s)\n",
val, hw->whoami, hw->name); return -ENODEV;
}
st->name = hw->name;
/* reset to make sure previous state are not there */
ret = regmap_write(st->map, INV_ICM42600_REG_DEVICE_CONFIG,
INV_ICM42600_DEVICE_CONFIG_SOFT_RESET); if (ret) return ret;
msleep(INV_ICM42600_RESET_TIME_MS);
ret = regmap_read(st->map, INV_ICM42600_REG_INT_STATUS, &val); if (ret) return ret; if (!(val & INV_ICM42600_INT_STATUS_RESET_DONE)) {
dev_err(dev, "reset error, reset done bit not set\n"); return -ENODEV;
}
/* set chip bus configuration */
ret = bus_setup(st); if (ret) return ret;
/* sensor data in big-endian (default) */
ret = regmap_set_bits(st->map, INV_ICM42600_REG_INTF_CONFIG0,
INV_ICM42600_INTF_CONFIG0_SENSOR_DATA_ENDIAN); if (ret) return ret;
/* * Use RC clock for accel low-power to fix glitches when switching * gyro on/off while accel low-power is on.
*/
ret = regmap_update_bits(st->map, INV_ICM42600_REG_INTF_CONFIG1,
INV_ICM42600_INTF_CONFIG1_ACCEL_LP_CLK_RC,
INV_ICM42600_INTF_CONFIG1_ACCEL_LP_CLK_RC); if (ret) return ret;
/** * inv_icm42600_irq_init() - initialize int pin and interrupt handler * @st: driver internal state * @irq: irq number * @irq_type: irq trigger type * @open_drain: true if irq is open drain, false for push-pull * * Returns 0 on success, a negative error code otherwise.
*/ staticint inv_icm42600_irq_init(struct inv_icm42600_state *st, int irq, int irq_type, bool open_drain)
{ struct device *dev = regmap_get_device(st->map); unsignedint val; int ret;
/* configure INT1 interrupt: default is active low on edge */ switch (irq_type) { case IRQF_TRIGGER_RISING: case IRQF_TRIGGER_HIGH:
val = INV_ICM42600_INT_CONFIG_INT1_ACTIVE_HIGH; break; default:
val = INV_ICM42600_INT_CONFIG_INT1_ACTIVE_LOW; break;
}
switch (irq_type) { case IRQF_TRIGGER_LOW: case IRQF_TRIGGER_HIGH:
val |= INV_ICM42600_INT_CONFIG_INT1_LATCHED; break; default: break;
}
if (!open_drain)
val |= INV_ICM42600_INT_CONFIG_INT1_PUSH_PULL;
ret = regmap_write(st->map, INV_ICM42600_REG_INT_CONFIG, val); if (ret) return ret;
/* Deassert async reset for proper INT pin operation (cf datasheet) */
ret = regmap_clear_bits(st->map, INV_ICM42600_REG_INT_CONFIG1,
INV_ICM42600_INT_CONFIG1_ASYNC_RESET); if (ret) return ret;
/* get INT1 only supported interrupt or fallback to first interrupt */
irq = fwnode_irq_get_byname(fwnode, "INT1"); if (irq < 0 && irq != -EPROBE_DEFER) {
dev_info(dev, "no INT1 interrupt defined, fallback to first interrupt\n");
irq = fwnode_irq_get(fwnode, 0);
} if (irq < 0) return dev_err_probe(dev, irq, "error missing INT1 interrupt\n");
irq_type = irq_get_trigger_type(irq); if (!irq_type)
irq_type = IRQF_TRIGGER_FALLING;
/* * Suspend saves sensors state and turns everything off. * Check first if runtime suspend has not already done the job.
*/ staticint inv_icm42600_suspend(struct device *dev)
{ struct inv_icm42600_state *st = dev_get_drvdata(dev); struct device *accel_dev; bool wakeup; int accel_conf; int ret = 0;
/* disable FIFO data streaming */ if (st->fifo.on) {
ret = regmap_write(st->map, INV_ICM42600_REG_FIFO_CONFIG,
INV_ICM42600_FIFO_CONFIG_BYPASS); if (ret) goto out_unlock;
}
/* keep chip on and wake-up capable if APEX and wakeup on */
accel_dev = &st->indio_accel->dev;
wakeup = st->apex.on && device_may_wakeup(accel_dev); if (wakeup) { /* keep accel on and setup irq for wakeup */
accel_conf = st->conf.accel.mode;
enable_irq_wake(st->irq);
disable_irq(st->irq);
} else { /* disable APEX features and accel if wakeup disabled */ if (st->apex.wom.enable) {
ret = inv_icm42600_disable_wom(st); if (ret) goto out_unlock;
}
accel_conf = INV_ICM42600_SENSOR_MODE_OFF;
}
ret = inv_icm42600_set_pwr_mgmt0(st, INV_ICM42600_SENSOR_MODE_OFF,
accel_conf, false, NULL); if (ret) goto out_unlock;
/* disable vddio regulator if chip is sleeping */ if (!wakeup)
regulator_disable(st->vddio_supply);
/* * System resume gets the system back on and restores the sensors state. * Manually put runtime power management in system active state.
*/ staticint inv_icm42600_resume(struct device *dev)
{ struct inv_icm42600_state *st = dev_get_drvdata(dev); struct inv_icm42600_sensor_state *gyro_st = iio_priv(st->indio_gyro); struct inv_icm42600_sensor_state *accel_st = iio_priv(st->indio_accel); struct device *accel_dev; bool wakeup; int ret = 0;
mutex_lock(&st->lock);
if (pm_runtime_suspended(dev)) goto out_unlock;
/* check wakeup capability */
accel_dev = &st->indio_accel->dev;
wakeup = st->apex.on && device_may_wakeup(accel_dev); /* restore irq state or vddio if cut off */ if (wakeup) {
enable_irq(st->irq);
disable_irq_wake(st->irq);
} else {
ret = inv_icm42600_enable_regulator_vddio(st); if (ret) goto out_unlock;
}
/* restore sensors state */
ret = inv_icm42600_set_pwr_mgmt0(st, st->suspended.gyro,
st->suspended.accel,
st->suspended.temp, NULL); if (ret) goto out_unlock;
/* restore APEX features if disabled */ if (!wakeup && st->apex.wom.enable) {
ret = inv_icm42600_enable_wom(st); if (ret) goto out_unlock;
}
/* restore FIFO data streaming */ if (st->fifo.on) {
inv_sensors_timestamp_reset(&gyro_st->ts);
inv_sensors_timestamp_reset(&accel_st->ts);
ret = regmap_write(st->map, INV_ICM42600_REG_FIFO_CONFIG,
INV_ICM42600_FIFO_CONFIG_STREAM);
}
/* Runtime suspend will turn off sensors that are enabled by iio devices. */ staticint inv_icm42600_runtime_suspend(struct device *dev)
{ struct inv_icm42600_state *st = dev_get_drvdata(dev); int ret;
mutex_lock(&st->lock);
/* disable all sensors */
ret = inv_icm42600_set_pwr_mgmt0(st, INV_ICM42600_SENSOR_MODE_OFF,
INV_ICM42600_SENSOR_MODE_OFF, false,
NULL); if (ret) goto error_unlock;
/* Sensors are enabled by iio devices, no need to turn them back on here. */ staticint inv_icm42600_runtime_resume(struct device *dev)
{ struct inv_icm42600_state *st = dev_get_drvdata(dev); int ret;
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.