162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * MMC35240 - MEMSIC 3-axis Magnetic Sensor
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2015, Intel Corporation.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * IIO driver for MMC35240 (7-bit I2C slave address 0x30).
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * TODO: offset, ACPI, continuous measurement mode, PM
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/init.h>
1462306a36Sopenharmony_ci#include <linux/i2c.h>
1562306a36Sopenharmony_ci#include <linux/delay.h>
1662306a36Sopenharmony_ci#include <linux/regmap.h>
1762306a36Sopenharmony_ci#include <linux/acpi.h>
1862306a36Sopenharmony_ci#include <linux/pm.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <linux/iio/iio.h>
2162306a36Sopenharmony_ci#include <linux/iio/sysfs.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define MMC35240_DRV_NAME "mmc35240"
2462306a36Sopenharmony_ci#define MMC35240_REGMAP_NAME "mmc35240_regmap"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define MMC35240_REG_XOUT_L	0x00
2762306a36Sopenharmony_ci#define MMC35240_REG_XOUT_H	0x01
2862306a36Sopenharmony_ci#define MMC35240_REG_YOUT_L	0x02
2962306a36Sopenharmony_ci#define MMC35240_REG_YOUT_H	0x03
3062306a36Sopenharmony_ci#define MMC35240_REG_ZOUT_L	0x04
3162306a36Sopenharmony_ci#define MMC35240_REG_ZOUT_H	0x05
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define MMC35240_REG_STATUS	0x06
3462306a36Sopenharmony_ci#define MMC35240_REG_CTRL0	0x07
3562306a36Sopenharmony_ci#define MMC35240_REG_CTRL1	0x08
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define MMC35240_REG_ID		0x20
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define MMC35240_STATUS_MEAS_DONE_BIT	BIT(0)
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define MMC35240_CTRL0_REFILL_BIT	BIT(7)
4262306a36Sopenharmony_ci#define MMC35240_CTRL0_RESET_BIT	BIT(6)
4362306a36Sopenharmony_ci#define MMC35240_CTRL0_SET_BIT		BIT(5)
4462306a36Sopenharmony_ci#define MMC35240_CTRL0_CMM_BIT		BIT(1)
4562306a36Sopenharmony_ci#define MMC35240_CTRL0_TM_BIT		BIT(0)
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* output resolution bits */
4862306a36Sopenharmony_ci#define MMC35240_CTRL1_BW0_BIT		BIT(0)
4962306a36Sopenharmony_ci#define MMC35240_CTRL1_BW1_BIT		BIT(1)
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#define MMC35240_CTRL1_BW_MASK	 (MMC35240_CTRL1_BW0_BIT | \
5262306a36Sopenharmony_ci		 MMC35240_CTRL1_BW1_BIT)
5362306a36Sopenharmony_ci#define MMC35240_CTRL1_BW_SHIFT		0
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define MMC35240_WAIT_CHARGE_PUMP	50000	/* us */
5662306a36Sopenharmony_ci#define MMC35240_WAIT_SET_RESET		1000	/* us */
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/*
5962306a36Sopenharmony_ci * Memsic OTP process code piece is put here for reference:
6062306a36Sopenharmony_ci *
6162306a36Sopenharmony_ci * #define OTP_CONVERT(REG)  ((float)((REG) >=32 ? (32 - (REG)) : (REG)) * 0.006
6262306a36Sopenharmony_ci * 1) For X axis, the COEFFICIENT is always 1.
6362306a36Sopenharmony_ci * 2) For Y axis, the COEFFICIENT is as below:
6462306a36Sopenharmony_ci *    f_OTP_matrix[4] = OTP_CONVERT(((reg_data[1] & 0x03) << 4) |
6562306a36Sopenharmony_ci *                                   (reg_data[2] >> 4)) + 1.0;
6662306a36Sopenharmony_ci * 3) For Z axis, the COEFFICIENT is as below:
6762306a36Sopenharmony_ci *    f_OTP_matrix[8] = (OTP_CONVERT(reg_data[3] & 0x3f) + 1) * 1.35;
6862306a36Sopenharmony_ci * We implemented the OTP logic into driver.
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/* scale = 1000 here for Y otp */
7262306a36Sopenharmony_ci#define MMC35240_OTP_CONVERT_Y(REG) (((REG) >= 32 ? (32 - (REG)) : (REG)) * 6)
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/* 0.6 * 1.35 = 0.81, scale 10000 for Z otp */
7562306a36Sopenharmony_ci#define MMC35240_OTP_CONVERT_Z(REG) (((REG) >= 32 ? (32 - (REG)) : (REG)) * 81)
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci#define MMC35240_X_COEFF(x)	(x)
7862306a36Sopenharmony_ci#define MMC35240_Y_COEFF(y)	(y + 1000)
7962306a36Sopenharmony_ci#define MMC35240_Z_COEFF(z)	(z + 13500)
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#define MMC35240_OTP_START_ADDR		0x1B
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cienum mmc35240_resolution {
8462306a36Sopenharmony_ci	MMC35240_16_BITS_SLOW = 0, /* 7.92 ms */
8562306a36Sopenharmony_ci	MMC35240_16_BITS_FAST,     /* 4.08 ms */
8662306a36Sopenharmony_ci	MMC35240_14_BITS,          /* 2.16 ms */
8762306a36Sopenharmony_ci	MMC35240_12_BITS,          /* 1.20 ms */
8862306a36Sopenharmony_ci};
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cienum mmc35240_axis {
9162306a36Sopenharmony_ci	AXIS_X = 0,
9262306a36Sopenharmony_ci	AXIS_Y,
9362306a36Sopenharmony_ci	AXIS_Z,
9462306a36Sopenharmony_ci};
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic const struct {
9762306a36Sopenharmony_ci	int sens[3]; /* sensitivity per X, Y, Z axis */
9862306a36Sopenharmony_ci	int nfo; /* null field output */
9962306a36Sopenharmony_ci} mmc35240_props_table[] = {
10062306a36Sopenharmony_ci	/* 16 bits, 125Hz ODR */
10162306a36Sopenharmony_ci	{
10262306a36Sopenharmony_ci		{1024, 1024, 1024},
10362306a36Sopenharmony_ci		32768,
10462306a36Sopenharmony_ci	},
10562306a36Sopenharmony_ci	/* 16 bits, 250Hz ODR */
10662306a36Sopenharmony_ci	{
10762306a36Sopenharmony_ci		{1024, 1024, 770},
10862306a36Sopenharmony_ci		32768,
10962306a36Sopenharmony_ci	},
11062306a36Sopenharmony_ci	/* 14 bits, 450Hz ODR */
11162306a36Sopenharmony_ci	{
11262306a36Sopenharmony_ci		{256, 256, 193},
11362306a36Sopenharmony_ci		8192,
11462306a36Sopenharmony_ci	},
11562306a36Sopenharmony_ci	/* 12 bits, 800Hz ODR */
11662306a36Sopenharmony_ci	{
11762306a36Sopenharmony_ci		{64, 64, 48},
11862306a36Sopenharmony_ci		2048,
11962306a36Sopenharmony_ci	},
12062306a36Sopenharmony_ci};
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistruct mmc35240_data {
12362306a36Sopenharmony_ci	struct i2c_client *client;
12462306a36Sopenharmony_ci	struct mutex mutex;
12562306a36Sopenharmony_ci	struct regmap *regmap;
12662306a36Sopenharmony_ci	enum mmc35240_resolution res;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/* OTP compensation */
12962306a36Sopenharmony_ci	int axis_coef[3];
13062306a36Sopenharmony_ci	int axis_scale[3];
13162306a36Sopenharmony_ci};
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic const struct {
13462306a36Sopenharmony_ci	int val;
13562306a36Sopenharmony_ci	int val2;
13662306a36Sopenharmony_ci} mmc35240_samp_freq[] = { {1, 500000},
13762306a36Sopenharmony_ci			   {13, 0},
13862306a36Sopenharmony_ci			   {25, 0},
13962306a36Sopenharmony_ci			   {50, 0} };
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1.5 13 25 50");
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci#define MMC35240_CHANNEL(_axis) { \
14462306a36Sopenharmony_ci	.type = IIO_MAGN, \
14562306a36Sopenharmony_ci	.modified = 1, \
14662306a36Sopenharmony_ci	.channel2 = IIO_MOD_ ## _axis, \
14762306a36Sopenharmony_ci	.address = AXIS_ ## _axis, \
14862306a36Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
14962306a36Sopenharmony_ci	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
15062306a36Sopenharmony_ci			BIT(IIO_CHAN_INFO_SCALE), \
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic const struct iio_chan_spec mmc35240_channels[] = {
15462306a36Sopenharmony_ci	MMC35240_CHANNEL(X),
15562306a36Sopenharmony_ci	MMC35240_CHANNEL(Y),
15662306a36Sopenharmony_ci	MMC35240_CHANNEL(Z),
15762306a36Sopenharmony_ci};
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic struct attribute *mmc35240_attributes[] = {
16062306a36Sopenharmony_ci	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
16162306a36Sopenharmony_ci	NULL
16262306a36Sopenharmony_ci};
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic const struct attribute_group mmc35240_attribute_group = {
16562306a36Sopenharmony_ci	.attrs = mmc35240_attributes,
16662306a36Sopenharmony_ci};
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic int mmc35240_get_samp_freq_index(struct mmc35240_data *data,
16962306a36Sopenharmony_ci					int val, int val2)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	int i;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mmc35240_samp_freq); i++)
17462306a36Sopenharmony_ci		if (mmc35240_samp_freq[i].val == val &&
17562306a36Sopenharmony_ci		    mmc35240_samp_freq[i].val2 == val2)
17662306a36Sopenharmony_ci			return i;
17762306a36Sopenharmony_ci	return -EINVAL;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic int mmc35240_hw_set(struct mmc35240_data *data, bool set)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	int ret;
18362306a36Sopenharmony_ci	u8 coil_bit;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	/*
18662306a36Sopenharmony_ci	 * Recharge the capacitor at VCAP pin, requested to be issued
18762306a36Sopenharmony_ci	 * before a SET/RESET command.
18862306a36Sopenharmony_ci	 */
18962306a36Sopenharmony_ci	ret = regmap_update_bits(data->regmap, MMC35240_REG_CTRL0,
19062306a36Sopenharmony_ci				 MMC35240_CTRL0_REFILL_BIT,
19162306a36Sopenharmony_ci				 MMC35240_CTRL0_REFILL_BIT);
19262306a36Sopenharmony_ci	if (ret < 0)
19362306a36Sopenharmony_ci		return ret;
19462306a36Sopenharmony_ci	usleep_range(MMC35240_WAIT_CHARGE_PUMP, MMC35240_WAIT_CHARGE_PUMP + 1);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	if (set)
19762306a36Sopenharmony_ci		coil_bit = MMC35240_CTRL0_SET_BIT;
19862306a36Sopenharmony_ci	else
19962306a36Sopenharmony_ci		coil_bit = MMC35240_CTRL0_RESET_BIT;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	return regmap_update_bits(data->regmap, MMC35240_REG_CTRL0,
20262306a36Sopenharmony_ci				  coil_bit, coil_bit);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic int mmc35240_init(struct mmc35240_data *data)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	int ret, y_convert, z_convert;
20962306a36Sopenharmony_ci	unsigned int reg_id;
21062306a36Sopenharmony_ci	u8 otp_data[6];
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	ret = regmap_read(data->regmap, MMC35240_REG_ID, &reg_id);
21362306a36Sopenharmony_ci	if (ret < 0) {
21462306a36Sopenharmony_ci		dev_err(&data->client->dev, "Error reading product id\n");
21562306a36Sopenharmony_ci		return ret;
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	dev_dbg(&data->client->dev, "MMC35240 chip id %x\n", reg_id);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/*
22162306a36Sopenharmony_ci	 * make sure we restore sensor characteristics, by doing
22262306a36Sopenharmony_ci	 * a SET/RESET sequence, the axis polarity being naturally
22362306a36Sopenharmony_ci	 * aligned after RESET
22462306a36Sopenharmony_ci	 */
22562306a36Sopenharmony_ci	ret = mmc35240_hw_set(data, true);
22662306a36Sopenharmony_ci	if (ret < 0)
22762306a36Sopenharmony_ci		return ret;
22862306a36Sopenharmony_ci	usleep_range(MMC35240_WAIT_SET_RESET, MMC35240_WAIT_SET_RESET + 1);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	ret = mmc35240_hw_set(data, false);
23162306a36Sopenharmony_ci	if (ret < 0)
23262306a36Sopenharmony_ci		return ret;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	/* set default sampling frequency */
23562306a36Sopenharmony_ci	ret = regmap_update_bits(data->regmap, MMC35240_REG_CTRL1,
23662306a36Sopenharmony_ci				 MMC35240_CTRL1_BW_MASK,
23762306a36Sopenharmony_ci				 data->res << MMC35240_CTRL1_BW_SHIFT);
23862306a36Sopenharmony_ci	if (ret < 0)
23962306a36Sopenharmony_ci		return ret;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	ret = regmap_bulk_read(data->regmap, MMC35240_OTP_START_ADDR,
24262306a36Sopenharmony_ci			       otp_data, sizeof(otp_data));
24362306a36Sopenharmony_ci	if (ret < 0)
24462306a36Sopenharmony_ci		return ret;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	y_convert = MMC35240_OTP_CONVERT_Y(((otp_data[1] & 0x03) << 4) |
24762306a36Sopenharmony_ci					   (otp_data[2] >> 4));
24862306a36Sopenharmony_ci	z_convert = MMC35240_OTP_CONVERT_Z(otp_data[3] & 0x3f);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	data->axis_coef[0] = MMC35240_X_COEFF(1);
25162306a36Sopenharmony_ci	data->axis_coef[1] = MMC35240_Y_COEFF(y_convert);
25262306a36Sopenharmony_ci	data->axis_coef[2] = MMC35240_Z_COEFF(z_convert);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	data->axis_scale[0] = 1;
25562306a36Sopenharmony_ci	data->axis_scale[1] = 1000;
25662306a36Sopenharmony_ci	data->axis_scale[2] = 10000;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	return 0;
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic int mmc35240_take_measurement(struct mmc35240_data *data)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	int ret, tries = 100;
26462306a36Sopenharmony_ci	unsigned int reg_status;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	ret = regmap_write(data->regmap, MMC35240_REG_CTRL0,
26762306a36Sopenharmony_ci			   MMC35240_CTRL0_TM_BIT);
26862306a36Sopenharmony_ci	if (ret < 0)
26962306a36Sopenharmony_ci		return ret;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	while (tries-- > 0) {
27262306a36Sopenharmony_ci		ret = regmap_read(data->regmap, MMC35240_REG_STATUS,
27362306a36Sopenharmony_ci				  &reg_status);
27462306a36Sopenharmony_ci		if (ret < 0)
27562306a36Sopenharmony_ci			return ret;
27662306a36Sopenharmony_ci		if (reg_status & MMC35240_STATUS_MEAS_DONE_BIT)
27762306a36Sopenharmony_ci			break;
27862306a36Sopenharmony_ci		/* minimum wait time to complete measurement is 10 ms */
27962306a36Sopenharmony_ci		usleep_range(10000, 11000);
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (tries < 0) {
28362306a36Sopenharmony_ci		dev_err(&data->client->dev, "data not ready\n");
28462306a36Sopenharmony_ci		return -EIO;
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	return 0;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic int mmc35240_read_measurement(struct mmc35240_data *data, __le16 buf[3])
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	int ret;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	ret = mmc35240_take_measurement(data);
29562306a36Sopenharmony_ci	if (ret < 0)
29662306a36Sopenharmony_ci		return ret;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	return regmap_bulk_read(data->regmap, MMC35240_REG_XOUT_L, buf,
29962306a36Sopenharmony_ci				3 * sizeof(__le16));
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci/**
30362306a36Sopenharmony_ci * mmc35240_raw_to_mgauss - convert raw readings to milli gauss. Also apply
30462306a36Sopenharmony_ci *			    compensation for output value.
30562306a36Sopenharmony_ci *
30662306a36Sopenharmony_ci * @data: device private data
30762306a36Sopenharmony_ci * @index: axis index for which we want the conversion
30862306a36Sopenharmony_ci * @buf: raw data to be converted, 2 bytes in little endian format
30962306a36Sopenharmony_ci * @val: compensated output reading (unit is milli gauss)
31062306a36Sopenharmony_ci *
31162306a36Sopenharmony_ci * Returns: 0 in case of success, -EINVAL when @index is not valid
31262306a36Sopenharmony_ci */
31362306a36Sopenharmony_cistatic int mmc35240_raw_to_mgauss(struct mmc35240_data *data, int index,
31462306a36Sopenharmony_ci				  __le16 buf[], int *val)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	int raw[3];
31762306a36Sopenharmony_ci	int sens[3];
31862306a36Sopenharmony_ci	int nfo;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	raw[AXIS_X] = le16_to_cpu(buf[AXIS_X]);
32162306a36Sopenharmony_ci	raw[AXIS_Y] = le16_to_cpu(buf[AXIS_Y]);
32262306a36Sopenharmony_ci	raw[AXIS_Z] = le16_to_cpu(buf[AXIS_Z]);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	sens[AXIS_X] = mmc35240_props_table[data->res].sens[AXIS_X];
32562306a36Sopenharmony_ci	sens[AXIS_Y] = mmc35240_props_table[data->res].sens[AXIS_Y];
32662306a36Sopenharmony_ci	sens[AXIS_Z] = mmc35240_props_table[data->res].sens[AXIS_Z];
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	nfo = mmc35240_props_table[data->res].nfo;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	switch (index) {
33162306a36Sopenharmony_ci	case AXIS_X:
33262306a36Sopenharmony_ci		*val = (raw[AXIS_X] - nfo) * 1000 / sens[AXIS_X];
33362306a36Sopenharmony_ci		break;
33462306a36Sopenharmony_ci	case AXIS_Y:
33562306a36Sopenharmony_ci		*val = (raw[AXIS_Y] - nfo) * 1000 / sens[AXIS_Y] -
33662306a36Sopenharmony_ci			(raw[AXIS_Z] - nfo)  * 1000 / sens[AXIS_Z];
33762306a36Sopenharmony_ci		break;
33862306a36Sopenharmony_ci	case AXIS_Z:
33962306a36Sopenharmony_ci		*val = (raw[AXIS_Y] - nfo) * 1000 / sens[AXIS_Y] +
34062306a36Sopenharmony_ci			(raw[AXIS_Z] - nfo) * 1000 / sens[AXIS_Z];
34162306a36Sopenharmony_ci		break;
34262306a36Sopenharmony_ci	default:
34362306a36Sopenharmony_ci		return -EINVAL;
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci	/* apply OTP compensation */
34662306a36Sopenharmony_ci	*val = (*val) * data->axis_coef[index] / data->axis_scale[index];
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	return 0;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic int mmc35240_read_raw(struct iio_dev *indio_dev,
35262306a36Sopenharmony_ci			     struct iio_chan_spec const *chan, int *val,
35362306a36Sopenharmony_ci			     int *val2, long mask)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	struct mmc35240_data *data = iio_priv(indio_dev);
35662306a36Sopenharmony_ci	int ret, i;
35762306a36Sopenharmony_ci	unsigned int reg;
35862306a36Sopenharmony_ci	__le16 buf[3];
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	switch (mask) {
36162306a36Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
36262306a36Sopenharmony_ci		mutex_lock(&data->mutex);
36362306a36Sopenharmony_ci		ret = mmc35240_read_measurement(data, buf);
36462306a36Sopenharmony_ci		mutex_unlock(&data->mutex);
36562306a36Sopenharmony_ci		if (ret < 0)
36662306a36Sopenharmony_ci			return ret;
36762306a36Sopenharmony_ci		ret = mmc35240_raw_to_mgauss(data, chan->address, buf, val);
36862306a36Sopenharmony_ci		if (ret < 0)
36962306a36Sopenharmony_ci			return ret;
37062306a36Sopenharmony_ci		return IIO_VAL_INT;
37162306a36Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
37262306a36Sopenharmony_ci		*val = 0;
37362306a36Sopenharmony_ci		*val2 = 1000;
37462306a36Sopenharmony_ci		return IIO_VAL_INT_PLUS_MICRO;
37562306a36Sopenharmony_ci	case IIO_CHAN_INFO_SAMP_FREQ:
37662306a36Sopenharmony_ci		mutex_lock(&data->mutex);
37762306a36Sopenharmony_ci		ret = regmap_read(data->regmap, MMC35240_REG_CTRL1, &reg);
37862306a36Sopenharmony_ci		mutex_unlock(&data->mutex);
37962306a36Sopenharmony_ci		if (ret < 0)
38062306a36Sopenharmony_ci			return ret;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci		i = (reg & MMC35240_CTRL1_BW_MASK) >> MMC35240_CTRL1_BW_SHIFT;
38362306a36Sopenharmony_ci		if (i < 0 || i >= ARRAY_SIZE(mmc35240_samp_freq))
38462306a36Sopenharmony_ci			return -EINVAL;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		*val = mmc35240_samp_freq[i].val;
38762306a36Sopenharmony_ci		*val2 = mmc35240_samp_freq[i].val2;
38862306a36Sopenharmony_ci		return IIO_VAL_INT_PLUS_MICRO;
38962306a36Sopenharmony_ci	default:
39062306a36Sopenharmony_ci		return -EINVAL;
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistatic int mmc35240_write_raw(struct iio_dev *indio_dev,
39562306a36Sopenharmony_ci			      struct iio_chan_spec const *chan, int val,
39662306a36Sopenharmony_ci			      int val2, long mask)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	struct mmc35240_data *data = iio_priv(indio_dev);
39962306a36Sopenharmony_ci	int i, ret;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	switch (mask) {
40262306a36Sopenharmony_ci	case IIO_CHAN_INFO_SAMP_FREQ:
40362306a36Sopenharmony_ci		i = mmc35240_get_samp_freq_index(data, val, val2);
40462306a36Sopenharmony_ci		if (i < 0)
40562306a36Sopenharmony_ci			return -EINVAL;
40662306a36Sopenharmony_ci		mutex_lock(&data->mutex);
40762306a36Sopenharmony_ci		ret = regmap_update_bits(data->regmap, MMC35240_REG_CTRL1,
40862306a36Sopenharmony_ci					 MMC35240_CTRL1_BW_MASK,
40962306a36Sopenharmony_ci					 i << MMC35240_CTRL1_BW_SHIFT);
41062306a36Sopenharmony_ci		mutex_unlock(&data->mutex);
41162306a36Sopenharmony_ci		return ret;
41262306a36Sopenharmony_ci	default:
41362306a36Sopenharmony_ci		return -EINVAL;
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic const struct iio_info mmc35240_info = {
41862306a36Sopenharmony_ci	.read_raw	= mmc35240_read_raw,
41962306a36Sopenharmony_ci	.write_raw	= mmc35240_write_raw,
42062306a36Sopenharmony_ci	.attrs		= &mmc35240_attribute_group,
42162306a36Sopenharmony_ci};
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic bool mmc35240_is_writeable_reg(struct device *dev, unsigned int reg)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	switch (reg) {
42662306a36Sopenharmony_ci	case MMC35240_REG_CTRL0:
42762306a36Sopenharmony_ci	case MMC35240_REG_CTRL1:
42862306a36Sopenharmony_ci		return true;
42962306a36Sopenharmony_ci	default:
43062306a36Sopenharmony_ci		return false;
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic bool mmc35240_is_readable_reg(struct device *dev, unsigned int reg)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	switch (reg) {
43762306a36Sopenharmony_ci	case MMC35240_REG_XOUT_L:
43862306a36Sopenharmony_ci	case MMC35240_REG_XOUT_H:
43962306a36Sopenharmony_ci	case MMC35240_REG_YOUT_L:
44062306a36Sopenharmony_ci	case MMC35240_REG_YOUT_H:
44162306a36Sopenharmony_ci	case MMC35240_REG_ZOUT_L:
44262306a36Sopenharmony_ci	case MMC35240_REG_ZOUT_H:
44362306a36Sopenharmony_ci	case MMC35240_REG_STATUS:
44462306a36Sopenharmony_ci	case MMC35240_REG_ID:
44562306a36Sopenharmony_ci		return true;
44662306a36Sopenharmony_ci	default:
44762306a36Sopenharmony_ci		return false;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cistatic bool mmc35240_is_volatile_reg(struct device *dev, unsigned int reg)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	switch (reg) {
45462306a36Sopenharmony_ci	case MMC35240_REG_CTRL0:
45562306a36Sopenharmony_ci	case MMC35240_REG_CTRL1:
45662306a36Sopenharmony_ci		return false;
45762306a36Sopenharmony_ci	default:
45862306a36Sopenharmony_ci		return true;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic const struct reg_default mmc35240_reg_defaults[] = {
46362306a36Sopenharmony_ci	{ MMC35240_REG_CTRL0,  0x00 },
46462306a36Sopenharmony_ci	{ MMC35240_REG_CTRL1,  0x00 },
46562306a36Sopenharmony_ci};
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic const struct regmap_config mmc35240_regmap_config = {
46862306a36Sopenharmony_ci	.name = MMC35240_REGMAP_NAME,
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	.reg_bits = 8,
47162306a36Sopenharmony_ci	.val_bits = 8,
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	.max_register = MMC35240_REG_ID,
47462306a36Sopenharmony_ci	.cache_type = REGCACHE_FLAT,
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	.writeable_reg = mmc35240_is_writeable_reg,
47762306a36Sopenharmony_ci	.readable_reg = mmc35240_is_readable_reg,
47862306a36Sopenharmony_ci	.volatile_reg = mmc35240_is_volatile_reg,
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	.reg_defaults = mmc35240_reg_defaults,
48162306a36Sopenharmony_ci	.num_reg_defaults = ARRAY_SIZE(mmc35240_reg_defaults),
48262306a36Sopenharmony_ci};
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic int mmc35240_probe(struct i2c_client *client)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	struct mmc35240_data *data;
48762306a36Sopenharmony_ci	struct iio_dev *indio_dev;
48862306a36Sopenharmony_ci	struct regmap *regmap;
48962306a36Sopenharmony_ci	int ret;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
49262306a36Sopenharmony_ci	if (!indio_dev)
49362306a36Sopenharmony_ci		return -ENOMEM;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	regmap = devm_regmap_init_i2c(client, &mmc35240_regmap_config);
49662306a36Sopenharmony_ci	if (IS_ERR(regmap)) {
49762306a36Sopenharmony_ci		dev_err(&client->dev, "regmap initialization failed\n");
49862306a36Sopenharmony_ci		return PTR_ERR(regmap);
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	data = iio_priv(indio_dev);
50262306a36Sopenharmony_ci	i2c_set_clientdata(client, indio_dev);
50362306a36Sopenharmony_ci	data->client = client;
50462306a36Sopenharmony_ci	data->regmap = regmap;
50562306a36Sopenharmony_ci	data->res = MMC35240_16_BITS_SLOW;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	mutex_init(&data->mutex);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	indio_dev->info = &mmc35240_info;
51062306a36Sopenharmony_ci	indio_dev->name = MMC35240_DRV_NAME;
51162306a36Sopenharmony_ci	indio_dev->channels = mmc35240_channels;
51262306a36Sopenharmony_ci	indio_dev->num_channels = ARRAY_SIZE(mmc35240_channels);
51362306a36Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	ret = mmc35240_init(data);
51662306a36Sopenharmony_ci	if (ret < 0) {
51762306a36Sopenharmony_ci		dev_err(&client->dev, "mmc35240 chip init failed\n");
51862306a36Sopenharmony_ci		return ret;
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci	return devm_iio_device_register(&client->dev, indio_dev);
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_cistatic int mmc35240_suspend(struct device *dev)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
52662306a36Sopenharmony_ci	struct mmc35240_data *data = iio_priv(indio_dev);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	regcache_cache_only(data->regmap, true);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	return 0;
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cistatic int mmc35240_resume(struct device *dev)
53462306a36Sopenharmony_ci{
53562306a36Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
53662306a36Sopenharmony_ci	struct mmc35240_data *data = iio_priv(indio_dev);
53762306a36Sopenharmony_ci	int ret;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	regcache_mark_dirty(data->regmap);
54062306a36Sopenharmony_ci	ret = regcache_sync_region(data->regmap, MMC35240_REG_CTRL0,
54162306a36Sopenharmony_ci				   MMC35240_REG_CTRL1);
54262306a36Sopenharmony_ci	if (ret < 0)
54362306a36Sopenharmony_ci		dev_err(dev, "Failed to restore control registers\n");
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	regcache_cache_only(data->regmap, false);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	return 0;
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(mmc35240_pm_ops, mmc35240_suspend,
55162306a36Sopenharmony_ci				mmc35240_resume);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_cistatic const struct of_device_id mmc35240_of_match[] = {
55462306a36Sopenharmony_ci	{ .compatible = "memsic,mmc35240", },
55562306a36Sopenharmony_ci	{ }
55662306a36Sopenharmony_ci};
55762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mmc35240_of_match);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic const struct acpi_device_id mmc35240_acpi_match[] = {
56062306a36Sopenharmony_ci	{"MMC35240", 0},
56162306a36Sopenharmony_ci	{ },
56262306a36Sopenharmony_ci};
56362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, mmc35240_acpi_match);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_cistatic const struct i2c_device_id mmc35240_id[] = {
56662306a36Sopenharmony_ci	{"mmc35240", 0},
56762306a36Sopenharmony_ci	{}
56862306a36Sopenharmony_ci};
56962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, mmc35240_id);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_cistatic struct i2c_driver mmc35240_driver = {
57262306a36Sopenharmony_ci	.driver = {
57362306a36Sopenharmony_ci		.name = MMC35240_DRV_NAME,
57462306a36Sopenharmony_ci		.of_match_table = mmc35240_of_match,
57562306a36Sopenharmony_ci		.pm = pm_sleep_ptr(&mmc35240_pm_ops),
57662306a36Sopenharmony_ci		.acpi_match_table = ACPI_PTR(mmc35240_acpi_match),
57762306a36Sopenharmony_ci	},
57862306a36Sopenharmony_ci	.probe		= mmc35240_probe,
57962306a36Sopenharmony_ci	.id_table	= mmc35240_id,
58062306a36Sopenharmony_ci};
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cimodule_i2c_driver(mmc35240_driver);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ciMODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
58562306a36Sopenharmony_ciMODULE_DESCRIPTION("MEMSIC MMC35240 magnetic sensor driver");
58662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
587