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			       &reg_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