162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for the TI TMAG5273 Low-Power Linear 3D Hall-Effect Sensor 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2022 WolfVision GmbH 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Gerald Loacker <gerald.loacker@wolfvision.net> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/bitfield.h> 1162306a36Sopenharmony_ci#include <linux/bits.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/i2c.h> 1562306a36Sopenharmony_ci#include <linux/regmap.h> 1662306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/iio/iio.h> 1962306a36Sopenharmony_ci#include <linux/iio/sysfs.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define TMAG5273_DEVICE_CONFIG_1 0x00 2262306a36Sopenharmony_ci#define TMAG5273_DEVICE_CONFIG_2 0x01 2362306a36Sopenharmony_ci#define TMAG5273_SENSOR_CONFIG_1 0x02 2462306a36Sopenharmony_ci#define TMAG5273_SENSOR_CONFIG_2 0x03 2562306a36Sopenharmony_ci#define TMAG5273_X_THR_CONFIG 0x04 2662306a36Sopenharmony_ci#define TMAG5273_Y_THR_CONFIG 0x05 2762306a36Sopenharmony_ci#define TMAG5273_Z_THR_CONFIG 0x06 2862306a36Sopenharmony_ci#define TMAG5273_T_CONFIG 0x07 2962306a36Sopenharmony_ci#define TMAG5273_INT_CONFIG_1 0x08 3062306a36Sopenharmony_ci#define TMAG5273_MAG_GAIN_CONFIG 0x09 3162306a36Sopenharmony_ci#define TMAG5273_MAG_OFFSET_CONFIG_1 0x0A 3262306a36Sopenharmony_ci#define TMAG5273_MAG_OFFSET_CONFIG_2 0x0B 3362306a36Sopenharmony_ci#define TMAG5273_I2C_ADDRESS 0x0C 3462306a36Sopenharmony_ci#define TMAG5273_DEVICE_ID 0x0D 3562306a36Sopenharmony_ci#define TMAG5273_MANUFACTURER_ID_LSB 0x0E 3662306a36Sopenharmony_ci#define TMAG5273_MANUFACTURER_ID_MSB 0x0F 3762306a36Sopenharmony_ci#define TMAG5273_T_MSB_RESULT 0x10 3862306a36Sopenharmony_ci#define TMAG5273_T_LSB_RESULT 0x11 3962306a36Sopenharmony_ci#define TMAG5273_X_MSB_RESULT 0x12 4062306a36Sopenharmony_ci#define TMAG5273_X_LSB_RESULT 0x13 4162306a36Sopenharmony_ci#define TMAG5273_Y_MSB_RESULT 0x14 4262306a36Sopenharmony_ci#define TMAG5273_Y_LSB_RESULT 0x15 4362306a36Sopenharmony_ci#define TMAG5273_Z_MSB_RESULT 0x16 4462306a36Sopenharmony_ci#define TMAG5273_Z_LSB_RESULT 0x17 4562306a36Sopenharmony_ci#define TMAG5273_CONV_STATUS 0x18 4662306a36Sopenharmony_ci#define TMAG5273_ANGLE_RESULT_MSB 0x19 4762306a36Sopenharmony_ci#define TMAG5273_ANGLE_RESULT_LSB 0x1A 4862306a36Sopenharmony_ci#define TMAG5273_MAGNITUDE_RESULT 0x1B 4962306a36Sopenharmony_ci#define TMAG5273_DEVICE_STATUS 0x1C 5062306a36Sopenharmony_ci#define TMAG5273_MAX_REG TMAG5273_DEVICE_STATUS 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define TMAG5273_AUTOSLEEP_DELAY_MS 5000 5362306a36Sopenharmony_ci#define TMAG5273_MAX_AVERAGE 32 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* 5662306a36Sopenharmony_ci * bits in the TMAG5273_MANUFACTURER_ID_LSB / MSB register 5762306a36Sopenharmony_ci * 16-bit unique manufacturer ID 0x49 / 0x54 = "TI" 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_ci#define TMAG5273_MANUFACTURER_ID 0x5449 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* bits in the TMAG5273_DEVICE_CONFIG_1 register */ 6262306a36Sopenharmony_ci#define TMAG5273_AVG_MODE_MASK GENMASK(4, 2) 6362306a36Sopenharmony_ci#define TMAG5273_AVG_1_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 0) 6462306a36Sopenharmony_ci#define TMAG5273_AVG_2_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 1) 6562306a36Sopenharmony_ci#define TMAG5273_AVG_4_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 2) 6662306a36Sopenharmony_ci#define TMAG5273_AVG_8_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 3) 6762306a36Sopenharmony_ci#define TMAG5273_AVG_16_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 4) 6862306a36Sopenharmony_ci#define TMAG5273_AVG_32_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 5) 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* bits in the TMAG5273_DEVICE_CONFIG_2 register */ 7162306a36Sopenharmony_ci#define TMAG5273_OP_MODE_MASK GENMASK(1, 0) 7262306a36Sopenharmony_ci#define TMAG5273_OP_MODE_STANDBY FIELD_PREP(TMAG5273_OP_MODE_MASK, 0) 7362306a36Sopenharmony_ci#define TMAG5273_OP_MODE_SLEEP FIELD_PREP(TMAG5273_OP_MODE_MASK, 1) 7462306a36Sopenharmony_ci#define TMAG5273_OP_MODE_CONT FIELD_PREP(TMAG5273_OP_MODE_MASK, 2) 7562306a36Sopenharmony_ci#define TMAG5273_OP_MODE_WAKEUP FIELD_PREP(TMAG5273_OP_MODE_MASK, 3) 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/* bits in the TMAG5273_SENSOR_CONFIG_1 register */ 7862306a36Sopenharmony_ci#define TMAG5273_MAG_CH_EN_MASK GENMASK(7, 4) 7962306a36Sopenharmony_ci#define TMAG5273_MAG_CH_EN_X_Y_Z 7 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* bits in the TMAG5273_SENSOR_CONFIG_2 register */ 8262306a36Sopenharmony_ci#define TMAG5273_Z_RANGE_MASK BIT(0) 8362306a36Sopenharmony_ci#define TMAG5273_X_Y_RANGE_MASK BIT(1) 8462306a36Sopenharmony_ci#define TMAG5273_ANGLE_EN_MASK GENMASK(3, 2) 8562306a36Sopenharmony_ci#define TMAG5273_ANGLE_EN_OFF 0 8662306a36Sopenharmony_ci#define TMAG5273_ANGLE_EN_X_Y 1 8762306a36Sopenharmony_ci#define TMAG5273_ANGLE_EN_Y_Z 2 8862306a36Sopenharmony_ci#define TMAG5273_ANGLE_EN_X_Z 3 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* bits in the TMAG5273_T_CONFIG register */ 9162306a36Sopenharmony_ci#define TMAG5273_T_CH_EN BIT(0) 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* bits in the TMAG5273_DEVICE_ID register */ 9462306a36Sopenharmony_ci#define TMAG5273_VERSION_MASK GENMASK(1, 0) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* bits in the TMAG5273_CONV_STATUS register */ 9762306a36Sopenharmony_ci#define TMAG5273_CONV_STATUS_COMPLETE BIT(0) 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cienum tmag5273_channels { 10062306a36Sopenharmony_ci TEMPERATURE = 0, 10162306a36Sopenharmony_ci AXIS_X, 10262306a36Sopenharmony_ci AXIS_Y, 10362306a36Sopenharmony_ci AXIS_Z, 10462306a36Sopenharmony_ci ANGLE, 10562306a36Sopenharmony_ci MAGNITUDE, 10662306a36Sopenharmony_ci}; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cienum tmag5273_scale_index { 10962306a36Sopenharmony_ci MAGN_RANGE_LOW = 0, 11062306a36Sopenharmony_ci MAGN_RANGE_HIGH, 11162306a36Sopenharmony_ci MAGN_RANGE_NUM 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* state container for the TMAG5273 driver */ 11562306a36Sopenharmony_cistruct tmag5273_data { 11662306a36Sopenharmony_ci struct device *dev; 11762306a36Sopenharmony_ci unsigned int devid; 11862306a36Sopenharmony_ci unsigned int version; 11962306a36Sopenharmony_ci char name[16]; 12062306a36Sopenharmony_ci unsigned int conv_avg; 12162306a36Sopenharmony_ci unsigned int scale; 12262306a36Sopenharmony_ci enum tmag5273_scale_index scale_index; 12362306a36Sopenharmony_ci unsigned int angle_measurement; 12462306a36Sopenharmony_ci struct regmap *map; 12562306a36Sopenharmony_ci struct regulator *vcc; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* 12862306a36Sopenharmony_ci * Locks the sensor for exclusive use during a measurement (which 12962306a36Sopenharmony_ci * involves several register transactions so the regmap lock is not 13062306a36Sopenharmony_ci * enough) so that measurements get serialized in a 13162306a36Sopenharmony_ci * first-come-first-serve manner. 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_ci struct mutex lock; 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic const char *const tmag5273_angle_names[] = { "off", "x-y", "y-z", "x-z" }; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/* 13962306a36Sopenharmony_ci * Averaging enables additional sampling of the sensor data to reduce the noise 14062306a36Sopenharmony_ci * effect, but also increases conversion time. 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_cistatic const unsigned int tmag5273_avg_table[] = { 14362306a36Sopenharmony_ci 1, 2, 4, 8, 16, 32, 14462306a36Sopenharmony_ci}; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci/* 14762306a36Sopenharmony_ci * Magnetic resolution in Gauss for different TMAG5273 versions. 14862306a36Sopenharmony_ci * Scale[Gauss] = Range[mT] * 1000 / 2^15 * 10, (1 mT = 10 Gauss) 14962306a36Sopenharmony_ci * Only version 1 and 2 are valid, version 0 and 3 are reserved. 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_cistatic const struct iio_val_int_plus_micro tmag5273_scale[][MAGN_RANGE_NUM] = { 15262306a36Sopenharmony_ci { { 0, 0 }, { 0, 0 } }, 15362306a36Sopenharmony_ci { { 0, 12200 }, { 0, 24400 } }, 15462306a36Sopenharmony_ci { { 0, 40600 }, { 0, 81200 } }, 15562306a36Sopenharmony_ci { { 0, 0 }, { 0, 0 } }, 15662306a36Sopenharmony_ci}; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic int tmag5273_get_measure(struct tmag5273_data *data, s16 *t, s16 *x, 15962306a36Sopenharmony_ci s16 *y, s16 *z, u16 *angle, u16 *magnitude) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci unsigned int status, val; 16262306a36Sopenharmony_ci __be16 reg_data[4]; 16362306a36Sopenharmony_ci int ret; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci mutex_lock(&data->lock); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* 16862306a36Sopenharmony_ci * Max. conversion time is 2425 us in 32x averaging mode for all three 16962306a36Sopenharmony_ci * channels. Since we are in continuous measurement mode, a measurement 17062306a36Sopenharmony_ci * may already be there, so poll for completed measurement with 17162306a36Sopenharmony_ci * timeout. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_ci ret = regmap_read_poll_timeout(data->map, TMAG5273_CONV_STATUS, status, 17462306a36Sopenharmony_ci status & TMAG5273_CONV_STATUS_COMPLETE, 17562306a36Sopenharmony_ci 100, 10000); 17662306a36Sopenharmony_ci if (ret) { 17762306a36Sopenharmony_ci dev_err(data->dev, "timeout waiting for measurement\n"); 17862306a36Sopenharmony_ci goto out_unlock; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci ret = regmap_bulk_read(data->map, TMAG5273_T_MSB_RESULT, reg_data, 18262306a36Sopenharmony_ci sizeof(reg_data)); 18362306a36Sopenharmony_ci if (ret) 18462306a36Sopenharmony_ci goto out_unlock; 18562306a36Sopenharmony_ci *t = be16_to_cpu(reg_data[0]); 18662306a36Sopenharmony_ci *x = be16_to_cpu(reg_data[1]); 18762306a36Sopenharmony_ci *y = be16_to_cpu(reg_data[2]); 18862306a36Sopenharmony_ci *z = be16_to_cpu(reg_data[3]); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci ret = regmap_bulk_read(data->map, TMAG5273_ANGLE_RESULT_MSB, 19162306a36Sopenharmony_ci ®_data[0], sizeof(reg_data[0])); 19262306a36Sopenharmony_ci if (ret) 19362306a36Sopenharmony_ci goto out_unlock; 19462306a36Sopenharmony_ci /* 19562306a36Sopenharmony_ci * angle has 9 bits integer value and 4 bits fractional part 19662306a36Sopenharmony_ci * 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 19762306a36Sopenharmony_ci * 0 0 0 a a a a a a a a a f f f f 19862306a36Sopenharmony_ci */ 19962306a36Sopenharmony_ci *angle = be16_to_cpu(reg_data[0]); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci ret = regmap_read(data->map, TMAG5273_MAGNITUDE_RESULT, &val); 20262306a36Sopenharmony_ci if (ret < 0) 20362306a36Sopenharmony_ci goto out_unlock; 20462306a36Sopenharmony_ci *magnitude = val; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ciout_unlock: 20762306a36Sopenharmony_ci mutex_unlock(&data->lock); 20862306a36Sopenharmony_ci return ret; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic int tmag5273_write_osr(struct tmag5273_data *data, int val) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci int i; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (val == data->conv_avg) 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tmag5273_avg_table); i++) { 21962306a36Sopenharmony_ci if (tmag5273_avg_table[i] == val) 22062306a36Sopenharmony_ci break; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci if (i == ARRAY_SIZE(tmag5273_avg_table)) 22362306a36Sopenharmony_ci return -EINVAL; 22462306a36Sopenharmony_ci data->conv_avg = val; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return regmap_update_bits(data->map, TMAG5273_DEVICE_CONFIG_1, 22762306a36Sopenharmony_ci TMAG5273_AVG_MODE_MASK, 22862306a36Sopenharmony_ci FIELD_PREP(TMAG5273_AVG_MODE_MASK, i)); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic int tmag5273_write_scale(struct tmag5273_data *data, int scale_micro) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci u32 value; 23462306a36Sopenharmony_ci int i; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tmag5273_scale[0]); i++) { 23762306a36Sopenharmony_ci if (tmag5273_scale[data->version][i].micro == scale_micro) 23862306a36Sopenharmony_ci break; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci if (i == ARRAY_SIZE(tmag5273_scale[0])) 24162306a36Sopenharmony_ci return -EINVAL; 24262306a36Sopenharmony_ci data->scale_index = i; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (data->scale_index == MAGN_RANGE_LOW) 24562306a36Sopenharmony_ci value = 0; 24662306a36Sopenharmony_ci else 24762306a36Sopenharmony_ci value = TMAG5273_Z_RANGE_MASK | TMAG5273_X_Y_RANGE_MASK; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci return regmap_update_bits(data->map, TMAG5273_SENSOR_CONFIG_2, 25062306a36Sopenharmony_ci TMAG5273_Z_RANGE_MASK | TMAG5273_X_Y_RANGE_MASK, value); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic int tmag5273_read_avail(struct iio_dev *indio_dev, 25462306a36Sopenharmony_ci struct iio_chan_spec const *chan, 25562306a36Sopenharmony_ci const int **vals, int *type, int *length, 25662306a36Sopenharmony_ci long mask) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct tmag5273_data *data = iio_priv(indio_dev); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci switch (mask) { 26162306a36Sopenharmony_ci case IIO_CHAN_INFO_OVERSAMPLING_RATIO: 26262306a36Sopenharmony_ci *vals = tmag5273_avg_table; 26362306a36Sopenharmony_ci *type = IIO_VAL_INT; 26462306a36Sopenharmony_ci *length = ARRAY_SIZE(tmag5273_avg_table); 26562306a36Sopenharmony_ci return IIO_AVAIL_LIST; 26662306a36Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 26762306a36Sopenharmony_ci switch (chan->type) { 26862306a36Sopenharmony_ci case IIO_MAGN: 26962306a36Sopenharmony_ci *type = IIO_VAL_INT_PLUS_MICRO; 27062306a36Sopenharmony_ci *vals = (int *)tmag5273_scale[data->version]; 27162306a36Sopenharmony_ci *length = ARRAY_SIZE(tmag5273_scale[data->version]) * 27262306a36Sopenharmony_ci MAGN_RANGE_NUM; 27362306a36Sopenharmony_ci return IIO_AVAIL_LIST; 27462306a36Sopenharmony_ci default: 27562306a36Sopenharmony_ci return -EINVAL; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci default: 27862306a36Sopenharmony_ci return -EINVAL; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic int tmag5273_read_raw(struct iio_dev *indio_dev, 28362306a36Sopenharmony_ci const struct iio_chan_spec *chan, int *val, 28462306a36Sopenharmony_ci int *val2, long mask) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct tmag5273_data *data = iio_priv(indio_dev); 28762306a36Sopenharmony_ci s16 t, x, y, z; 28862306a36Sopenharmony_ci u16 angle, magnitude; 28962306a36Sopenharmony_ci int ret; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci switch (mask) { 29262306a36Sopenharmony_ci case IIO_CHAN_INFO_PROCESSED: 29362306a36Sopenharmony_ci case IIO_CHAN_INFO_RAW: 29462306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(data->dev); 29562306a36Sopenharmony_ci if (ret < 0) 29662306a36Sopenharmony_ci return ret; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci ret = tmag5273_get_measure(data, &t, &x, &y, &z, &angle, &magnitude); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci pm_runtime_mark_last_busy(data->dev); 30162306a36Sopenharmony_ci pm_runtime_put_autosuspend(data->dev); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (ret) 30462306a36Sopenharmony_ci return ret; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci switch (chan->address) { 30762306a36Sopenharmony_ci case TEMPERATURE: 30862306a36Sopenharmony_ci *val = t; 30962306a36Sopenharmony_ci return IIO_VAL_INT; 31062306a36Sopenharmony_ci case AXIS_X: 31162306a36Sopenharmony_ci *val = x; 31262306a36Sopenharmony_ci return IIO_VAL_INT; 31362306a36Sopenharmony_ci case AXIS_Y: 31462306a36Sopenharmony_ci *val = y; 31562306a36Sopenharmony_ci return IIO_VAL_INT; 31662306a36Sopenharmony_ci case AXIS_Z: 31762306a36Sopenharmony_ci *val = z; 31862306a36Sopenharmony_ci return IIO_VAL_INT; 31962306a36Sopenharmony_ci case ANGLE: 32062306a36Sopenharmony_ci *val = angle; 32162306a36Sopenharmony_ci return IIO_VAL_INT; 32262306a36Sopenharmony_ci case MAGNITUDE: 32362306a36Sopenharmony_ci *val = magnitude; 32462306a36Sopenharmony_ci return IIO_VAL_INT; 32562306a36Sopenharmony_ci default: 32662306a36Sopenharmony_ci return -EINVAL; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 32962306a36Sopenharmony_ci switch (chan->type) { 33062306a36Sopenharmony_ci case IIO_TEMP: 33162306a36Sopenharmony_ci /* 33262306a36Sopenharmony_ci * Convert device specific value to millicelsius. 33362306a36Sopenharmony_ci * Resolution from the sensor is 60.1 LSB/celsius and 33462306a36Sopenharmony_ci * the reference value at 25 celsius is 17508 LSBs. 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_ci *val = 10000; 33762306a36Sopenharmony_ci *val2 = 601; 33862306a36Sopenharmony_ci return IIO_VAL_FRACTIONAL; 33962306a36Sopenharmony_ci case IIO_MAGN: 34062306a36Sopenharmony_ci /* Magnetic resolution in uT */ 34162306a36Sopenharmony_ci *val = 0; 34262306a36Sopenharmony_ci *val2 = tmag5273_scale[data->version] 34362306a36Sopenharmony_ci [data->scale_index].micro; 34462306a36Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 34562306a36Sopenharmony_ci case IIO_ANGL: 34662306a36Sopenharmony_ci /* 34762306a36Sopenharmony_ci * Angle is in degrees and has four fractional bits, 34862306a36Sopenharmony_ci * therefore use 1/16 * pi/180 to convert to radians. 34962306a36Sopenharmony_ci */ 35062306a36Sopenharmony_ci *val = 1000; 35162306a36Sopenharmony_ci *val2 = 916732; 35262306a36Sopenharmony_ci return IIO_VAL_FRACTIONAL; 35362306a36Sopenharmony_ci default: 35462306a36Sopenharmony_ci return -EINVAL; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci case IIO_CHAN_INFO_OFFSET: 35762306a36Sopenharmony_ci switch (chan->type) { 35862306a36Sopenharmony_ci case IIO_TEMP: 35962306a36Sopenharmony_ci *val = -16005; 36062306a36Sopenharmony_ci return IIO_VAL_INT; 36162306a36Sopenharmony_ci default: 36262306a36Sopenharmony_ci return -EINVAL; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci case IIO_CHAN_INFO_OVERSAMPLING_RATIO: 36562306a36Sopenharmony_ci *val = data->conv_avg; 36662306a36Sopenharmony_ci return IIO_VAL_INT; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci default: 36962306a36Sopenharmony_ci return -EINVAL; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic int tmag5273_write_raw(struct iio_dev *indio_dev, 37462306a36Sopenharmony_ci struct iio_chan_spec const *chan, int val, 37562306a36Sopenharmony_ci int val2, long mask) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct tmag5273_data *data = iio_priv(indio_dev); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci switch (mask) { 38062306a36Sopenharmony_ci case IIO_CHAN_INFO_OVERSAMPLING_RATIO: 38162306a36Sopenharmony_ci return tmag5273_write_osr(data, val); 38262306a36Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 38362306a36Sopenharmony_ci switch (chan->type) { 38462306a36Sopenharmony_ci case IIO_MAGN: 38562306a36Sopenharmony_ci if (val) 38662306a36Sopenharmony_ci return -EINVAL; 38762306a36Sopenharmony_ci return tmag5273_write_scale(data, val2); 38862306a36Sopenharmony_ci default: 38962306a36Sopenharmony_ci return -EINVAL; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci default: 39262306a36Sopenharmony_ci return -EINVAL; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci#define TMAG5273_AXIS_CHANNEL(axis, index) \ 39762306a36Sopenharmony_ci { \ 39862306a36Sopenharmony_ci .type = IIO_MAGN, \ 39962306a36Sopenharmony_ci .modified = 1, \ 40062306a36Sopenharmony_ci .channel2 = IIO_MOD_##axis, \ 40162306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ 40262306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), \ 40362306a36Sopenharmony_ci .info_mask_shared_by_type_available = \ 40462306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), \ 40562306a36Sopenharmony_ci .info_mask_shared_by_all = \ 40662306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ 40762306a36Sopenharmony_ci .info_mask_shared_by_all_available = \ 40862306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ 40962306a36Sopenharmony_ci .address = index, \ 41062306a36Sopenharmony_ci .scan_index = index, \ 41162306a36Sopenharmony_ci .scan_type = { \ 41262306a36Sopenharmony_ci .sign = 's', \ 41362306a36Sopenharmony_ci .realbits = 16, \ 41462306a36Sopenharmony_ci .storagebits = 16, \ 41562306a36Sopenharmony_ci .endianness = IIO_CPU, \ 41662306a36Sopenharmony_ci }, \ 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic const struct iio_chan_spec tmag5273_channels[] = { 42062306a36Sopenharmony_ci { 42162306a36Sopenharmony_ci .type = IIO_TEMP, 42262306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 42362306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE) | 42462306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_OFFSET), 42562306a36Sopenharmony_ci .address = TEMPERATURE, 42662306a36Sopenharmony_ci .scan_index = TEMPERATURE, 42762306a36Sopenharmony_ci .scan_type = { 42862306a36Sopenharmony_ci .sign = 'u', 42962306a36Sopenharmony_ci .realbits = 16, 43062306a36Sopenharmony_ci .storagebits = 16, 43162306a36Sopenharmony_ci .endianness = IIO_CPU, 43262306a36Sopenharmony_ci }, 43362306a36Sopenharmony_ci }, 43462306a36Sopenharmony_ci TMAG5273_AXIS_CHANNEL(X, AXIS_X), 43562306a36Sopenharmony_ci TMAG5273_AXIS_CHANNEL(Y, AXIS_Y), 43662306a36Sopenharmony_ci TMAG5273_AXIS_CHANNEL(Z, AXIS_Z), 43762306a36Sopenharmony_ci { 43862306a36Sopenharmony_ci .type = IIO_ANGL, 43962306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 44062306a36Sopenharmony_ci .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), 44162306a36Sopenharmony_ci .info_mask_shared_by_all = 44262306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), 44362306a36Sopenharmony_ci .info_mask_shared_by_all_available = 44462306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), 44562306a36Sopenharmony_ci .address = ANGLE, 44662306a36Sopenharmony_ci .scan_index = ANGLE, 44762306a36Sopenharmony_ci .scan_type = { 44862306a36Sopenharmony_ci .sign = 'u', 44962306a36Sopenharmony_ci .realbits = 16, 45062306a36Sopenharmony_ci .storagebits = 16, 45162306a36Sopenharmony_ci .endianness = IIO_CPU, 45262306a36Sopenharmony_ci }, 45362306a36Sopenharmony_ci }, 45462306a36Sopenharmony_ci { 45562306a36Sopenharmony_ci .type = IIO_DISTANCE, 45662306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 45762306a36Sopenharmony_ci .info_mask_shared_by_all = 45862306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), 45962306a36Sopenharmony_ci .info_mask_shared_by_all_available = 46062306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), 46162306a36Sopenharmony_ci .address = MAGNITUDE, 46262306a36Sopenharmony_ci .scan_index = MAGNITUDE, 46362306a36Sopenharmony_ci .scan_type = { 46462306a36Sopenharmony_ci .sign = 'u', 46562306a36Sopenharmony_ci .realbits = 16, 46662306a36Sopenharmony_ci .storagebits = 16, 46762306a36Sopenharmony_ci .endianness = IIO_CPU, 46862306a36Sopenharmony_ci }, 46962306a36Sopenharmony_ci }, 47062306a36Sopenharmony_ci IIO_CHAN_SOFT_TIMESTAMP(6), 47162306a36Sopenharmony_ci}; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic const struct iio_info tmag5273_info = { 47462306a36Sopenharmony_ci .read_avail = tmag5273_read_avail, 47562306a36Sopenharmony_ci .read_raw = tmag5273_read_raw, 47662306a36Sopenharmony_ci .write_raw = tmag5273_write_raw, 47762306a36Sopenharmony_ci}; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic bool tmag5273_volatile_reg(struct device *dev, unsigned int reg) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci return reg >= TMAG5273_T_MSB_RESULT && reg <= TMAG5273_MAGNITUDE_RESULT; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic const struct regmap_config tmag5273_regmap_config = { 48562306a36Sopenharmony_ci .reg_bits = 8, 48662306a36Sopenharmony_ci .val_bits = 8, 48762306a36Sopenharmony_ci .max_register = TMAG5273_MAX_REG, 48862306a36Sopenharmony_ci .volatile_reg = tmag5273_volatile_reg, 48962306a36Sopenharmony_ci}; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic int tmag5273_set_operating_mode(struct tmag5273_data *data, 49262306a36Sopenharmony_ci unsigned int val) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci return regmap_write(data->map, TMAG5273_DEVICE_CONFIG_2, val); 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic void tmag5273_read_device_property(struct tmag5273_data *data) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci struct device *dev = data->dev; 50062306a36Sopenharmony_ci const char *str; 50162306a36Sopenharmony_ci int ret; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci data->angle_measurement = TMAG5273_ANGLE_EN_X_Y; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci ret = device_property_read_string(dev, "ti,angle-measurement", &str); 50662306a36Sopenharmony_ci if (ret) 50762306a36Sopenharmony_ci return; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci ret = match_string(tmag5273_angle_names, 51062306a36Sopenharmony_ci ARRAY_SIZE(tmag5273_angle_names), str); 51162306a36Sopenharmony_ci if (ret >= 0) 51262306a36Sopenharmony_ci data->angle_measurement = ret; 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic void tmag5273_wake_up(struct tmag5273_data *data) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci int val; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* Wake up the chip by sending a dummy I2C command */ 52062306a36Sopenharmony_ci regmap_read(data->map, TMAG5273_DEVICE_ID, &val); 52162306a36Sopenharmony_ci /* 52262306a36Sopenharmony_ci * Time to go to stand-by mode from sleep mode is 50us 52362306a36Sopenharmony_ci * typically, during this time no I2C access is possible. 52462306a36Sopenharmony_ci */ 52562306a36Sopenharmony_ci usleep_range(80, 200); 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic int tmag5273_chip_init(struct tmag5273_data *data) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci int ret; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci ret = regmap_write(data->map, TMAG5273_DEVICE_CONFIG_1, 53362306a36Sopenharmony_ci TMAG5273_AVG_32_MODE); 53462306a36Sopenharmony_ci if (ret) 53562306a36Sopenharmony_ci return ret; 53662306a36Sopenharmony_ci data->conv_avg = 32; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci ret = regmap_write(data->map, TMAG5273_DEVICE_CONFIG_2, 53962306a36Sopenharmony_ci TMAG5273_OP_MODE_CONT); 54062306a36Sopenharmony_ci if (ret) 54162306a36Sopenharmony_ci return ret; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci ret = regmap_write(data->map, TMAG5273_SENSOR_CONFIG_1, 54462306a36Sopenharmony_ci FIELD_PREP(TMAG5273_MAG_CH_EN_MASK, 54562306a36Sopenharmony_ci TMAG5273_MAG_CH_EN_X_Y_Z)); 54662306a36Sopenharmony_ci if (ret) 54762306a36Sopenharmony_ci return ret; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci ret = regmap_write(data->map, TMAG5273_SENSOR_CONFIG_2, 55062306a36Sopenharmony_ci FIELD_PREP(TMAG5273_ANGLE_EN_MASK, 55162306a36Sopenharmony_ci data->angle_measurement)); 55262306a36Sopenharmony_ci if (ret) 55362306a36Sopenharmony_ci return ret; 55462306a36Sopenharmony_ci data->scale_index = MAGN_RANGE_LOW; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci return regmap_write(data->map, TMAG5273_T_CONFIG, TMAG5273_T_CH_EN); 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic int tmag5273_check_device_id(struct tmag5273_data *data) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci __le16 devid; 56262306a36Sopenharmony_ci int val, ret; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci ret = regmap_read(data->map, TMAG5273_DEVICE_ID, &val); 56562306a36Sopenharmony_ci if (ret) 56662306a36Sopenharmony_ci return dev_err_probe(data->dev, ret, "failed to power on device\n"); 56762306a36Sopenharmony_ci data->version = FIELD_PREP(TMAG5273_VERSION_MASK, val); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci ret = regmap_bulk_read(data->map, TMAG5273_MANUFACTURER_ID_LSB, &devid, 57062306a36Sopenharmony_ci sizeof(devid)); 57162306a36Sopenharmony_ci if (ret) 57262306a36Sopenharmony_ci return dev_err_probe(data->dev, ret, "failed to read device ID\n"); 57362306a36Sopenharmony_ci data->devid = le16_to_cpu(devid); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci switch (data->devid) { 57662306a36Sopenharmony_ci case TMAG5273_MANUFACTURER_ID: 57762306a36Sopenharmony_ci /* 57862306a36Sopenharmony_ci * The device name matches the orderable part number. 'x' stands 57962306a36Sopenharmony_ci * for A, B, C or D devices, which have different I2C addresses. 58062306a36Sopenharmony_ci * Versions 1 or 2 (0 and 3 is reserved) stands for different 58162306a36Sopenharmony_ci * magnetic strengths. 58262306a36Sopenharmony_ci */ 58362306a36Sopenharmony_ci snprintf(data->name, sizeof(data->name), "tmag5273x%1u", data->version); 58462306a36Sopenharmony_ci if (data->version < 1 || data->version > 2) 58562306a36Sopenharmony_ci dev_warn(data->dev, "Unsupported device %s\n", data->name); 58662306a36Sopenharmony_ci return 0; 58762306a36Sopenharmony_ci default: 58862306a36Sopenharmony_ci /* 58962306a36Sopenharmony_ci * Only print warning in case of unknown device ID to allow 59062306a36Sopenharmony_ci * fallback compatible in device tree. 59162306a36Sopenharmony_ci */ 59262306a36Sopenharmony_ci dev_warn(data->dev, "Unknown device ID 0x%x\n", data->devid); 59362306a36Sopenharmony_ci return 0; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic void tmag5273_power_down(void *data) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci tmag5273_set_operating_mode(data, TMAG5273_OP_MODE_SLEEP); 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic int tmag5273_probe(struct i2c_client *i2c) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci struct device *dev = &i2c->dev; 60562306a36Sopenharmony_ci struct tmag5273_data *data; 60662306a36Sopenharmony_ci struct iio_dev *indio_dev; 60762306a36Sopenharmony_ci int ret; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); 61062306a36Sopenharmony_ci if (!indio_dev) 61162306a36Sopenharmony_ci return -ENOMEM; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci data = iio_priv(indio_dev); 61462306a36Sopenharmony_ci data->dev = dev; 61562306a36Sopenharmony_ci i2c_set_clientdata(i2c, indio_dev); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci data->map = devm_regmap_init_i2c(i2c, &tmag5273_regmap_config); 61862306a36Sopenharmony_ci if (IS_ERR(data->map)) 61962306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(data->map), 62062306a36Sopenharmony_ci "failed to allocate register map\n"); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci mutex_init(&data->lock); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci ret = devm_regulator_get_enable(dev, "vcc"); 62562306a36Sopenharmony_ci if (ret) 62662306a36Sopenharmony_ci return dev_err_probe(dev, ret, "failed to enable regulator\n"); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci tmag5273_wake_up(data); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci ret = tmag5273_check_device_id(data); 63162306a36Sopenharmony_ci if (ret) 63262306a36Sopenharmony_ci return ret; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci ret = tmag5273_set_operating_mode(data, TMAG5273_OP_MODE_CONT); 63562306a36Sopenharmony_ci if (ret) 63662306a36Sopenharmony_ci return dev_err_probe(dev, ret, "failed to power on device\n"); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci /* 63962306a36Sopenharmony_ci * Register powerdown deferred callback which suspends the chip 64062306a36Sopenharmony_ci * after module unloaded. 64162306a36Sopenharmony_ci * 64262306a36Sopenharmony_ci * TMAG5273 should be in SUSPEND mode in the two cases: 64362306a36Sopenharmony_ci * 1) When driver is loaded, but we do not have any data or 64462306a36Sopenharmony_ci * configuration requests to it (we are solving it using 64562306a36Sopenharmony_ci * autosuspend feature). 64662306a36Sopenharmony_ci * 2) When driver is unloaded and device is not used (devm action is 64762306a36Sopenharmony_ci * used in this case). 64862306a36Sopenharmony_ci */ 64962306a36Sopenharmony_ci ret = devm_add_action_or_reset(dev, tmag5273_power_down, data); 65062306a36Sopenharmony_ci if (ret) 65162306a36Sopenharmony_ci return dev_err_probe(dev, ret, "failed to add powerdown action\n"); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci ret = pm_runtime_set_active(dev); 65462306a36Sopenharmony_ci if (ret < 0) 65562306a36Sopenharmony_ci return ret; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci ret = devm_pm_runtime_enable(dev); 65862306a36Sopenharmony_ci if (ret) 65962306a36Sopenharmony_ci return ret; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci pm_runtime_get_noresume(dev); 66262306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(dev, TMAG5273_AUTOSLEEP_DELAY_MS); 66362306a36Sopenharmony_ci pm_runtime_use_autosuspend(dev); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci tmag5273_read_device_property(data); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci ret = tmag5273_chip_init(data); 66862306a36Sopenharmony_ci if (ret) 66962306a36Sopenharmony_ci return dev_err_probe(dev, ret, "failed to init device\n"); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci indio_dev->info = &tmag5273_info; 67262306a36Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 67362306a36Sopenharmony_ci indio_dev->name = data->name; 67462306a36Sopenharmony_ci indio_dev->channels = tmag5273_channels; 67562306a36Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(tmag5273_channels); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci pm_runtime_mark_last_busy(dev); 67862306a36Sopenharmony_ci pm_runtime_put_autosuspend(dev); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci ret = devm_iio_device_register(dev, indio_dev); 68162306a36Sopenharmony_ci if (ret) 68262306a36Sopenharmony_ci return dev_err_probe(dev, ret, "device register failed\n"); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci return 0; 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic int tmag5273_runtime_suspend(struct device *dev) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci struct iio_dev *indio_dev = dev_get_drvdata(dev); 69062306a36Sopenharmony_ci struct tmag5273_data *data = iio_priv(indio_dev); 69162306a36Sopenharmony_ci int ret; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci ret = tmag5273_set_operating_mode(data, TMAG5273_OP_MODE_SLEEP); 69462306a36Sopenharmony_ci if (ret) 69562306a36Sopenharmony_ci dev_err(dev, "failed to power off device (%pe)\n", ERR_PTR(ret)); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci return ret; 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_cistatic int tmag5273_runtime_resume(struct device *dev) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci struct iio_dev *indio_dev = dev_get_drvdata(dev); 70362306a36Sopenharmony_ci struct tmag5273_data *data = iio_priv(indio_dev); 70462306a36Sopenharmony_ci int ret; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci tmag5273_wake_up(data); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci ret = tmag5273_set_operating_mode(data, TMAG5273_OP_MODE_CONT); 70962306a36Sopenharmony_ci if (ret) 71062306a36Sopenharmony_ci dev_err(dev, "failed to power on device (%pe)\n", ERR_PTR(ret)); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci return ret; 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic DEFINE_RUNTIME_DEV_PM_OPS(tmag5273_pm_ops, 71662306a36Sopenharmony_ci tmag5273_runtime_suspend, tmag5273_runtime_resume, 71762306a36Sopenharmony_ci NULL); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic const struct i2c_device_id tmag5273_id[] = { 72062306a36Sopenharmony_ci { "tmag5273" }, 72162306a36Sopenharmony_ci { /* sentinel */ } 72262306a36Sopenharmony_ci}; 72362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tmag5273_id); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_cistatic const struct of_device_id tmag5273_of_match[] = { 72662306a36Sopenharmony_ci { .compatible = "ti,tmag5273" }, 72762306a36Sopenharmony_ci { /* sentinel */ } 72862306a36Sopenharmony_ci}; 72962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tmag5273_of_match); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic struct i2c_driver tmag5273_driver = { 73262306a36Sopenharmony_ci .driver = { 73362306a36Sopenharmony_ci .name = "tmag5273", 73462306a36Sopenharmony_ci .of_match_table = tmag5273_of_match, 73562306a36Sopenharmony_ci .pm = pm_ptr(&tmag5273_pm_ops), 73662306a36Sopenharmony_ci }, 73762306a36Sopenharmony_ci .probe = tmag5273_probe, 73862306a36Sopenharmony_ci .id_table = tmag5273_id, 73962306a36Sopenharmony_ci}; 74062306a36Sopenharmony_cimodule_i2c_driver(tmag5273_driver); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ciMODULE_DESCRIPTION("TI TMAG5273 Low-Power Linear 3D Hall-Effect Sensor driver"); 74362306a36Sopenharmony_ciMODULE_AUTHOR("Gerald Loacker <gerald.loacker@wolfvision.net>"); 74462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 745