#define AK8974_INT_CTRL_XEN BIT(7) /* Enable interrupt for this axis */ #define AK8974_INT_CTRL_YEN BIT(6) #define AK8974_INT_CTRL_ZEN BIT(5) #define AK8974_INT_CTRL_XYZEN (BIT(7)|BIT(6)|BIT(5)) #define AK8974_INT_CTRL_POL BIT(3) /* 0 = active low; 1 = active high */ #define AK8974_INT_CTRL_PULSE BIT(1) /* 0 = latched; 1 = pulse (50 usec) */ #define AK8974_INT_CTRL_RESDEF (AK8974_INT_CTRL_XYZEN | AK8974_INT_CTRL_POL)
/* HSCDTD008A-specific control register */ #define HSCDTD008A_CTRL4 0x1E #define HSCDTD008A_CTRL4_MMD BIT(7) /* must be set to 1 */ #define HSCDTD008A_CTRL4_RANGE BIT(4) /* 0 = 14-bit output; 1 = 15-bit output */ #define HSCDTD008A_CTRL4_RESDEF (HSCDTD008A_CTRL4_MMD | HSCDTD008A_CTRL4_RANGE)
/* The AMI305 has elaborate FW version and serial number registers */ #define AMI305_VER 0xE8 #define AMI305_SN 0xEA
#define AK8974_MAX_RANGE 2048
#define AK8974_POWERON_DELAY 50 #define AK8974_ACTIVATE_DELAY 1 #define AK8974_SELFTEST_DELAY 1 /* * Set the autosuspend to two orders of magnitude larger than the poweron * delay to make sane reasonable power tradeoff savings (5 seconds in * this case).
*/ #define AK8974_AUTOSUSPEND_DELAY 5000
#define AK8974_MEASTIME 3
#define AK8974_PWR_ON 1 #define AK8974_PWR_OFF 0
/** * struct ak8974 - state container for the AK8974 driver * @i2c: parent I2C client * @orientation: mounting matrix, flipped axis etc * @map: regmap to access the AK8974 registers over I2C * @regs: the avdd and dvdd power regulators * @name: the name of the part * @variant: the whoami ID value (for selecting code paths) * @lock: locks the magnetometer for exclusive use during a measurement * @drdy_irq: uses the DRDY IRQ line * @drdy_complete: completion for DRDY * @drdy_active_low: the DRDY IRQ is active low * @scan: timestamps
*/ struct ak8974 { struct i2c_client *i2c; struct iio_mount_matrix orientation; struct regmap *map; struct regulator_bulk_data regs[2]; constchar *name;
u8 variant; struct mutex lock; bool drdy_irq; struct completion drdy_complete; bool drdy_active_low; /* Ensure timestamp is naturally aligned */ struct {
__le16 channels[3];
aligned_s64 ts;
} scan;
};
val = mode ? AK8974_CTRL1_POWER : 0;
val |= AK8974_CTRL1_FORCE_EN;
ret = regmap_write(ak8974->map, AK8974_CTRL1, val); if (ret < 0) return ret;
if (mode)
msleep(AK8974_ACTIVATE_DELAY);
return 0;
}
staticint ak8974_reset(struct ak8974 *ak8974)
{ int ret;
/* Power on to get register access. Sets CTRL1 reg to reset state */
ret = ak8974_set_power(ak8974, AK8974_PWR_ON); if (ret) return ret;
ret = regmap_write(ak8974->map, AK8974_CTRL2, AK8974_CTRL2_RESDEF); if (ret) return ret;
ret = regmap_write(ak8974->map, AK8974_CTRL3, AK8974_CTRL3_RESDEF); if (ret) return ret; if (ak8974->variant != AK8974_WHOAMI_VALUE_HSCDTD008A) {
ret = regmap_write(ak8974->map, AK8974_INT_CTRL,
AK8974_INT_CTRL_RESDEF); if (ret) return ret;
} else {
ret = regmap_write(ak8974->map, HSCDTD008A_CTRL4,
HSCDTD008A_CTRL4_RESDEF); if (ret) return ret;
}
/* After reset, power off is default state */ return ak8974_set_power(ak8974, AK8974_PWR_OFF);
}
staticint ak8974_configure(struct ak8974 *ak8974)
{ int ret;
ret = regmap_write(ak8974->map, AK8974_CTRL2, AK8974_CTRL2_DRDY_EN |
AK8974_CTRL2_INT_EN); if (ret) return ret;
ret = regmap_write(ak8974->map, AK8974_CTRL3, 0); if (ret) return ret; if (ak8974->variant == AK8974_WHOAMI_VALUE_AMI306) { /* magic from datasheet: set high-speed measurement mode */
ret = ak8974_set_u16_val(ak8974, AMI306_CTRL4, 0xA07E); if (ret) return ret;
} if (ak8974->variant == AK8974_WHOAMI_VALUE_HSCDTD008A) return 0;
ret = regmap_write(ak8974->map, AK8974_INT_CTRL, AK8974_INT_CTRL_POL); if (ret) return ret;
/* Clear any previous measurement overflow status */
ret = regmap_read(ak8974->map, AK8974_INT_CLEAR, &clear); if (ret) return ret;
/* If we have a DRDY IRQ line, use it */ if (ak8974->drdy_irq) {
mask = AK8974_CTRL2_INT_EN |
AK8974_CTRL2_DRDY_EN |
AK8974_CTRL2_DRDY_POL;
val = AK8974_CTRL2_DRDY_EN;
if (!ak8974->drdy_active_low)
val |= AK8974_CTRL2_DRDY_POL;
init_completion(&ak8974->drdy_complete);
ret = regmap_update_bits(ak8974->map, AK8974_CTRL2,
mask, val); if (ret) return ret;
}
/* Force a measurement */ return regmap_set_bits(ak8974->map, AK8974_CTRL3, AK8974_CTRL3_FORCE);
}
staticint ak8974_await_drdy(struct ak8974 *ak8974)
{ int timeout = 2; unsignedint val; int ret;
if (ak8974->drdy_irq) {
ret = wait_for_completion_timeout(&ak8974->drdy_complete,
1 + msecs_to_jiffies(1000)); if (!ret) {
dev_err(&ak8974->i2c->dev, "timeout waiting for DRDY IRQ\n"); return -ETIMEDOUT;
} return 0;
}
/* Default delay-based poll loop */ do {
msleep(AK8974_MEASTIME);
ret = regmap_read(ak8974->map, AK8974_STATUS, &val); if (ret < 0) return ret; if (val & AK8974_STATUS_DRDY) return 0;
} while (--timeout);
dev_err(&ak8974->i2c->dev, "timeout waiting for DRDY\n"); return -ETIMEDOUT;
}
ret = ak8974_await_drdy(ak8974); if (ret) return ret;
ret = regmap_read(ak8974->map, AK8974_INT_SRC, &src); if (ret < 0) return ret;
/* Out of range overflow! Strong magnet close? */ if (src & AK8974_INT_RANGE) {
dev_err(&ak8974->i2c->dev, "range overflow in sensor\n"); return -ERANGE;
}
ret = regmap_bulk_read(ak8974->map, AK8974_DATA_X, result, 6); if (ret) return ret;
/* Check if this was a DRDY from us */
ret = regmap_read(ak8974->map, AK8974_STATUS, &val); if (ret < 0) {
dev_err(&ak8974->i2c->dev, "error reading DRDY status\n"); return IRQ_HANDLED;
} if (val & AK8974_STATUS_DRDY) { /* Yes this was our IRQ */
complete(&ak8974->drdy_complete); return IRQ_HANDLED;
}
/* We may be on a shared IRQ, let the next client check */ return IRQ_NONE;
}
ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val); if (ret) return ret; if (val != AK8974_SELFTEST_IDLE) {
dev_err(dev, "selftest not idle before test\n"); return -EIO;
}
/* Trigger self-test */
ret = regmap_set_bits(ak8974->map, AK8974_CTRL3, AK8974_CTRL3_SELFTEST); if (ret) {
dev_err(dev, "could not write CTRL3\n"); return ret;
}
msleep(AK8974_SELFTEST_DELAY);
ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val); if (ret) return ret; if (val != AK8974_SELFTEST_OK) {
dev_err(dev, "selftest result NOT OK (%02x)\n", val); return -EIO;
}
ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val); if (ret) return ret; if (val != AK8974_SELFTEST_IDLE) {
dev_err(dev, "selftest not idle after test (%02x)\n", val); return -EIO;
}
dev_dbg(dev, "passed self-test\n");
/* * We read all axes and discard all but one, for optimized * reading, use the triggered buffer.
*/
ret = ak8974_trigmeas(ak8974); if (ret) goto out_unlock;
ret = ak8974_getresult(ak8974, hw_values); if (ret) goto out_unlock; /* * This explicit cast to (s16) is necessary as the measurement * is done in 2's complement with positive and negative values. * The follwing assignment to *val will then convert the signed * s16 value to a signed int value.
*/
*val = (s16)le16_to_cpu(hw_values[address]);
out_unlock:
mutex_unlock(&ak8974->lock);
pm_runtime_mark_last_busy(&ak8974->i2c->dev);
pm_runtime_put_autosuspend(&ak8974->i2c->dev);
return ret;
}
staticint ak8974_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask)
{ struct ak8974 *ak8974 = iio_priv(indio_dev); int ret;
switch (mask) { case IIO_CHAN_INFO_RAW: if (chan->address > 2) {
dev_err(&ak8974->i2c->dev, "faulty channel address\n"); return -EIO;
}
ret = ak8974_measure_channel(ak8974, chan->address, val); if (ret) return ret; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: switch (ak8974->variant) { case AK8974_WHOAMI_VALUE_AMI306: case AK8974_WHOAMI_VALUE_AMI305: /* * The datasheet for AMI305 and AMI306, page 6 * specifies the range of the sensor to be * +/- 12 Gauss.
*/
*val = 12; /* * 12 bits are used, +/- 2^11 * [ -2048 .. 2047 ] (manual page 20) * [ 0xf800 .. 0x07ff ]
*/
*val2 = 11; return IIO_VAL_FRACTIONAL_LOG2; case AK8974_WHOAMI_VALUE_HSCDTD008A: /* * The datasheet for HSCDTF008A, page 3 specifies the * range of the sensor as +/- 2.4 mT per axis, which * corresponds to +/- 2400 uT = +/- 24 Gauss.
*/
*val = 24; /* * 15 bits are used (set up in CTRL4), +/- 2^14 * [ -16384 .. 16383 ] (manual page 24) * [ 0xc000 .. 0x3fff ]
*/
*val2 = 14; return IIO_VAL_FRACTIONAL_LOG2; default: /* GUESSING +/- 12 Gauss */
*val = 12; /* GUESSING 12 bits ADC +/- 2^11 */
*val2 = 11; return IIO_VAL_FRACTIONAL_LOG2;
} break; default: /* Unknown request */ break;
}
/* * We have no datasheet for the AK8974 but we guess that its * ADC is 12 bits. The AMI305 and AMI306 certainly has 12bit * ADC.
*/ staticconststruct iio_chan_spec ak8974_12_bits_channels[] = {
AK8974_AXIS_CHANNEL(X, 0, 12),
AK8974_AXIS_CHANNEL(Y, 1, 12),
AK8974_AXIS_CHANNEL(Z, 2, 12),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
/* * The HSCDTD008A has 15 bits resolution the way we set it up * in CTRL4.
*/ staticconststruct iio_chan_spec ak8974_15_bits_channels[] = {
AK8974_AXIS_CHANNEL(X, 0, 15),
AK8974_AXIS_CHANNEL(Y, 1, 15),
AK8974_AXIS_CHANNEL(Z, 2, 15),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
switch (reg) { case AK8974_CTRL1: case AK8974_CTRL2: case AK8974_CTRL3: case AK8974_INT_CTRL: case AK8974_INT_THRES: case AK8974_INT_THRES + 1: returntrue; case AK8974_PRESET: case AK8974_PRESET + 1: return ak8974->variant != AK8974_WHOAMI_VALUE_HSCDTD008A; case AK8974_OFFSET_X: case AK8974_OFFSET_X + 1: case AK8974_OFFSET_Y: case AK8974_OFFSET_Y + 1: case AK8974_OFFSET_Z: case AK8974_OFFSET_Z + 1: return ak8974->variant == AK8974_WHOAMI_VALUE_AK8974 ||
ak8974->variant == AK8974_WHOAMI_VALUE_HSCDTD008A; case AMI305_OFFSET_X: case AMI305_OFFSET_X + 1: case AMI305_OFFSET_Y: case AMI305_OFFSET_Y + 1: case AMI305_OFFSET_Z: case AMI305_OFFSET_Z + 1: return ak8974->variant == AK8974_WHOAMI_VALUE_AMI305 ||
ak8974->variant == AK8974_WHOAMI_VALUE_AMI306; case AMI306_CTRL4: case AMI306_CTRL4 + 1: return ak8974->variant == AK8974_WHOAMI_VALUE_AMI306; default: returnfalse;
}
}
ret = devm_regulator_bulk_get(&i2c->dev,
ARRAY_SIZE(ak8974->regs),
ak8974->regs); if (ret < 0) return dev_err_probe(&i2c->dev, ret, "cannot get regulators\n");
ret = regulator_bulk_enable(ARRAY_SIZE(ak8974->regs), ak8974->regs); if (ret < 0) {
dev_err(&i2c->dev, "cannot enable regulators\n"); return ret;
}
/* Take runtime PM online */
pm_runtime_get_noresume(&i2c->dev);
pm_runtime_set_active(&i2c->dev);
pm_runtime_enable(&i2c->dev);
ak8974->map = devm_regmap_init_i2c(i2c, &ak8974_regmap_config); if (IS_ERR(ak8974->map)) {
dev_err(&i2c->dev, "failed to allocate register map\n");
pm_runtime_put_noidle(&i2c->dev);
pm_runtime_disable(&i2c->dev); return PTR_ERR(ak8974->map);
}
ret = ak8974_set_power(ak8974, AK8974_PWR_ON); if (ret) {
dev_err(&i2c->dev, "could not power on\n"); goto disable_pm;
}
ret = ak8974_detect(ak8974); if (ret) {
dev_err(&i2c->dev, "neither AK8974 nor AMI30x found\n"); goto disable_pm;
}
ret = ak8974_selftest(ak8974); if (ret)
dev_err(&i2c->dev, "selftest failed (continuing anyway)\n");
ret = ak8974_reset(ak8974); if (ret) {
dev_err(&i2c->dev, "AK8974 reset failed\n"); goto disable_pm;
}
ret = regulator_bulk_enable(ARRAY_SIZE(ak8974->regs), ak8974->regs); if (ret) return ret;
msleep(AK8974_POWERON_DELAY);
ret = ak8974_set_power(ak8974, AK8974_PWR_ON); if (ret) goto out_regulator_disable;
ret = ak8974_configure(ak8974); if (ret) goto out_disable_power;
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.