/* state container for the TMAG5273 driver */ struct tmag5273_data { struct device *dev; unsignedint devid; unsignedint version; char name[16]; unsignedint conv_avg; enum tmag5273_scale_index scale_index; unsignedint angle_measurement; struct regmap *map;
/* * Locks the sensor for exclusive use during a measurement (which * involves several register transactions so the regmap lock is not * enough) so that measurements get serialized in a * first-come-first-serve manner.
*/ struct mutex lock;
};
/* * Averaging enables additional sampling of the sensor data to reduce the noise * effect, but also increases conversion time.
*/ staticconstunsignedint tmag5273_avg_table[] = {
1, 2, 4, 8, 16, 32,
};
/* * Magnetic resolution in Gauss for different TMAG5273 versions. * Scale[Gauss] = Range[mT] * 1000 / 2^15 * 10, (1 mT = 10 Gauss) * Only version 1 and 2 are valid, version 0 and 3 are reserved.
*/ staticconststruct iio_val_int_plus_micro tmag5273_scale[][MAGN_RANGE_NUM] = {
{ { 0, 0 }, { 0, 0 } },
{ { 0, 12200 }, { 0, 24400 } },
{ { 0, 40600 }, { 0, 81200 } },
{ { 0, 0 }, { 0, 0 } },
};
/* * Max. conversion time is 2425 us in 32x averaging mode for all three * channels. Since we are in continuous measurement mode, a measurement * may already be there, so poll for completed measurement with * timeout.
*/
ret = regmap_read_poll_timeout(data->map, TMAG5273_CONV_STATUS, status,
status & TMAG5273_CONV_STATUS_COMPLETE,
100, 10000); if (ret) {
dev_err(data->dev, "timeout waiting for measurement\n"); goto out_unlock;
}
ret = regmap_bulk_read(data->map, TMAG5273_ANGLE_RESULT_MSB,
®_data[0], sizeof(reg_data[0])); if (ret) goto out_unlock; /* * angle has 9 bits integer value and 4 bits fractional part * 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 * 0 0 0 a a a a a a a a a f f f f
*/
*angle = be16_to_cpu(reg_data[0]);
ret = regmap_read(data->map, TMAG5273_MAGNITUDE_RESULT, &val); if (ret < 0) goto out_unlock;
*magnitude = val;
staticint tmag5273_write_osr(struct tmag5273_data *data, int val)
{ int i;
if (val == data->conv_avg) return 0;
for (i = 0; i < ARRAY_SIZE(tmag5273_avg_table); i++) { if (tmag5273_avg_table[i] == val) break;
} if (i == ARRAY_SIZE(tmag5273_avg_table)) return -EINVAL;
data->conv_avg = val;
staticint tmag5273_write_scale(struct tmag5273_data *data, int scale_micro)
{
u32 value; int i;
for (i = 0; i < ARRAY_SIZE(tmag5273_scale[0]); i++) { if (tmag5273_scale[data->version][i].micro == scale_micro) break;
} if (i == ARRAY_SIZE(tmag5273_scale[0])) return -EINVAL;
data->scale_index = i;
if (data->scale_index == MAGN_RANGE_LOW)
value = 0; else
value = TMAG5273_Z_RANGE_MASK | TMAG5273_X_Y_RANGE_MASK;
switch (chan->address) { case TEMPERATURE:
*val = t; return IIO_VAL_INT; case AXIS_X:
*val = x; return IIO_VAL_INT; case AXIS_Y:
*val = y; return IIO_VAL_INT; case AXIS_Z:
*val = z; return IIO_VAL_INT; case ANGLE:
*val = angle; return IIO_VAL_INT; case MAGNITUDE:
*val = magnitude; return IIO_VAL_INT; default: return -EINVAL;
} case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_TEMP: /* * Convert device specific value to millicelsius. * Resolution from the sensor is 60.1 LSB/celsius and * the reference value at 25 celsius is 17508 LSBs.
*/
*val = 10000;
*val2 = 601; return IIO_VAL_FRACTIONAL; case IIO_MAGN: /* Magnetic resolution in uT */
*val = 0;
*val2 = tmag5273_scale[data->version]
[data->scale_index].micro; return IIO_VAL_INT_PLUS_MICRO; case IIO_ANGL: /* * Angle is in degrees and has four fractional bits, * therefore use 1/16 * pi/180 to convert to radians.
*/
*val = 1000;
*val2 = 916732; return IIO_VAL_FRACTIONAL; default: return -EINVAL;
} case IIO_CHAN_INFO_OFFSET: switch (chan->type) { case IIO_TEMP:
*val = -16005; return IIO_VAL_INT; default: return -EINVAL;
} case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
*val = data->conv_avg; return IIO_VAL_INT;
default: return -EINVAL;
}
}
staticint tmag5273_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask)
{ struct tmag5273_data *data = iio_priv(indio_dev);
switch (mask) { case IIO_CHAN_INFO_OVERSAMPLING_RATIO: return tmag5273_write_osr(data, val); case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_MAGN: if (val) return -EINVAL; return tmag5273_write_scale(data, val2); default: return -EINVAL;
} default: return -EINVAL;
}
}
ret = device_property_match_property_string(dev, "ti,angle-measurement",
tmag5273_angle_names,
ARRAY_SIZE(tmag5273_angle_names)); if (ret >= 0)
data->angle_measurement = ret;
}
staticvoid tmag5273_wake_up(struct tmag5273_data *data)
{ int val;
/* Wake up the chip by sending a dummy I2C command */
regmap_read(data->map, TMAG5273_DEVICE_ID, &val); /* * Time to go to stand-by mode from sleep mode is 50us * typically, during this time no I2C access is possible.
*/
usleep_range(80, 200);
}
staticint tmag5273_chip_init(struct tmag5273_data *data)
{ int ret;
ret = regmap_write(data->map, TMAG5273_DEVICE_CONFIG_1,
TMAG5273_AVG_32_MODE); if (ret) return ret;
data->conv_avg = 32;
ret = regmap_write(data->map, TMAG5273_DEVICE_CONFIG_2,
TMAG5273_OP_MODE_CONT); if (ret) return ret;
ret = regmap_write(data->map, TMAG5273_SENSOR_CONFIG_1,
FIELD_PREP(TMAG5273_MAG_CH_EN_MASK,
TMAG5273_MAG_CH_EN_X_Y_Z)); if (ret) return ret;
ret = regmap_write(data->map, TMAG5273_SENSOR_CONFIG_2,
FIELD_PREP(TMAG5273_ANGLE_EN_MASK,
data->angle_measurement)); if (ret) return ret;
data->scale_index = MAGN_RANGE_LOW;
staticint tmag5273_check_device_id(struct tmag5273_data *data)
{
__le16 devid; int val, ret;
ret = regmap_read(data->map, TMAG5273_DEVICE_ID, &val); if (ret) return dev_err_probe(data->dev, ret, "failed to power on device\n");
data->version = FIELD_PREP(TMAG5273_VERSION_MASK, val);
ret = regmap_bulk_read(data->map, TMAG5273_MANUFACTURER_ID_LSB, &devid, sizeof(devid)); if (ret) return dev_err_probe(data->dev, ret, "failed to read device ID\n");
data->devid = le16_to_cpu(devid);
switch (data->devid) { case TMAG5273_MANUFACTURER_ID: /* * The device name matches the orderable part number. 'x' stands * for A, B, C or D devices, which have different I2C addresses. * Versions 1 or 2 (0 and 3 is reserved) stands for different * magnetic strengths.
*/
snprintf(data->name, sizeof(data->name), "tmag5273x%1u", data->version); if (data->version < 1 || data->version > 2)
dev_warn(data->dev, "Unsupported device %s\n", data->name); return 0; default: /* * Only print warning in case of unknown device ID to allow * fallback compatible in device tree.
*/
dev_warn(data->dev, "Unknown device ID 0x%x\n", data->devid); return 0;
}
}
indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) return -ENOMEM;
data = iio_priv(indio_dev);
data->dev = dev;
i2c_set_clientdata(i2c, indio_dev);
data->map = devm_regmap_init_i2c(i2c, &tmag5273_regmap_config); if (IS_ERR(data->map)) return dev_err_probe(dev, PTR_ERR(data->map), "failed to allocate register map\n");
mutex_init(&data->lock);
ret = devm_regulator_get_enable(dev, "vcc"); if (ret) return dev_err_probe(dev, ret, "failed to enable regulator\n");
tmag5273_wake_up(data);
ret = tmag5273_check_device_id(data); if (ret) return ret;
ret = tmag5273_set_operating_mode(data, TMAG5273_OP_MODE_CONT); if (ret) return dev_err_probe(dev, ret, "failed to power on device\n");
/* * Register powerdown deferred callback which suspends the chip * after module unloaded. * * TMAG5273 should be in SUSPEND mode in the two cases: * 1) When driver is loaded, but we do not have any data or * configuration requests to it (we are solving it using * autosuspend feature). * 2) When driver is unloaded and device is not used (devm action is * used in this case).
*/
ret = devm_add_action_or_reset(dev, tmag5273_power_down, data); if (ret) return dev_err_probe(dev, ret, "failed to add powerdown action\n");
ret = pm_runtime_set_active(dev); if (ret < 0) return ret;
ret = devm_pm_runtime_enable(dev); if (ret) return 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.