xref: /linux/drivers/iio/accel/mma8452.c (revision ed5c2f5fd10dda07263f79f338a512c0f49f76f5)
180e3f010SMartin Kepplinger // SPDX-License-Identifier: GPL-2.0
2c7eeea93SPeter Meerwald /*
3f26ab1aaSMartin Kepplinger  * mma8452.c - Support for following Freescale / NXP 3-axis accelerometers:
4c5ea1b58SMartin Kepplinger  *
516df666aSMartin Kepplinger  * device name	digital output	7-bit I2C slave address (pin selectable)
616df666aSMartin Kepplinger  * ---------------------------------------------------------------------
716df666aSMartin Kepplinger  * MMA8451Q	14 bit		0x1c / 0x1d
816df666aSMartin Kepplinger  * MMA8452Q	12 bit		0x1c / 0x1d
916df666aSMartin Kepplinger  * MMA8453Q	10 bit		0x1c / 0x1d
1016df666aSMartin Kepplinger  * MMA8652FC	12 bit		0x1d
1116df666aSMartin Kepplinger  * MMA8653FC	10 bit		0x1d
1216df666aSMartin Kepplinger  * FXLS8471Q	14 bit		0x1e / 0x1d / 0x1c / 0x1f
13c7eeea93SPeter Meerwald  *
1440836bc3SMartin Kepplinger  * Copyright 2015 Martin Kepplinger <martink@posteo.de>
15c7eeea93SPeter Meerwald  * Copyright 2014 Peter Meerwald <pmeerw@pmeerw.net>
16c7eeea93SPeter Meerwald  *
17c7eeea93SPeter Meerwald  *
18bce59b60SMartin Kepplinger  * TODO: orientation events
19c7eeea93SPeter Meerwald  */
20c7eeea93SPeter Meerwald 
21c7eeea93SPeter Meerwald #include <linux/module.h>
22c7eeea93SPeter Meerwald #include <linux/i2c.h>
23c7eeea93SPeter Meerwald #include <linux/iio/iio.h>
24c7eeea93SPeter Meerwald #include <linux/iio/sysfs.h>
25c7eeea93SPeter Meerwald #include <linux/iio/buffer.h>
26ae6d9ce0SMartin Fuzzey #include <linux/iio/trigger.h>
27ae6d9ce0SMartin Fuzzey #include <linux/iio/trigger_consumer.h>
28c7eeea93SPeter Meerwald #include <linux/iio/triggered_buffer.h>
2928e34278SMartin Fuzzey #include <linux/iio/events.h>
30c7eeea93SPeter Meerwald #include <linux/delay.h>
31c3cdd6e4SMartin Kepplinger #include <linux/of_device.h>
32d2a3e093SMartin Kepplinger #include <linux/of_irq.h>
3396c0cb2bSMartin Kepplinger #include <linux/pm_runtime.h>
34f6ff49b8SAnson Huang #include <linux/regulator/consumer.h>
35c7eeea93SPeter Meerwald 
36c7eeea93SPeter Meerwald #define MMA8452_STATUS				0x00
3769abff81SHartmut Knaack #define  MMA8452_STATUS_DRDY			(BIT(2) | BIT(1) | BIT(0))
38c5ea1b58SMartin Kepplinger #define MMA8452_OUT_X				0x01 /* MSB first */
39c7eeea93SPeter Meerwald #define MMA8452_OUT_Y				0x03
40c7eeea93SPeter Meerwald #define MMA8452_OUT_Z				0x05
4128e34278SMartin Fuzzey #define MMA8452_INT_SRC				0x0c
42c7eeea93SPeter Meerwald #define MMA8452_WHO_AM_I			0x0d
43c7eeea93SPeter Meerwald #define MMA8452_DATA_CFG			0x0e
4469abff81SHartmut Knaack #define  MMA8452_DATA_CFG_FS_MASK		GENMASK(1, 0)
4569abff81SHartmut Knaack #define  MMA8452_DATA_CFG_FS_2G			0
4669abff81SHartmut Knaack #define  MMA8452_DATA_CFG_FS_4G			1
4769abff81SHartmut Knaack #define  MMA8452_DATA_CFG_FS_8G			2
4869abff81SHartmut Knaack #define  MMA8452_DATA_CFG_HPF_MASK		BIT(4)
491e79841aSMartin Fuzzey #define MMA8452_HP_FILTER_CUTOFF		0x0f
5069abff81SHartmut Knaack #define  MMA8452_HP_FILTER_CUTOFF_SEL_MASK	GENMASK(1, 0)
5160f562e7SMartin Kepplinger #define MMA8452_FF_MT_CFG			0x15
5260f562e7SMartin Kepplinger #define  MMA8452_FF_MT_CFG_OAE			BIT(6)
5360f562e7SMartin Kepplinger #define  MMA8452_FF_MT_CFG_ELE			BIT(7)
5460f562e7SMartin Kepplinger #define MMA8452_FF_MT_SRC			0x16
5560f562e7SMartin Kepplinger #define  MMA8452_FF_MT_SRC_XHE			BIT(1)
5660f562e7SMartin Kepplinger #define  MMA8452_FF_MT_SRC_YHE			BIT(3)
5760f562e7SMartin Kepplinger #define  MMA8452_FF_MT_SRC_ZHE			BIT(5)
5860f562e7SMartin Kepplinger #define MMA8452_FF_MT_THS			0x17
5960f562e7SMartin Kepplinger #define  MMA8452_FF_MT_THS_MASK			0x7f
6060f562e7SMartin Kepplinger #define MMA8452_FF_MT_COUNT			0x18
61605f72deSHarinath Nampally #define MMA8452_FF_MT_CHAN_SHIFT		3
6228e34278SMartin Fuzzey #define MMA8452_TRANSIENT_CFG			0x1d
63605f72deSHarinath Nampally #define  MMA8452_TRANSIENT_CFG_CHAN(chan)	BIT(chan + 1)
641e79841aSMartin Fuzzey #define  MMA8452_TRANSIENT_CFG_HPF_BYP		BIT(0)
6569abff81SHartmut Knaack #define  MMA8452_TRANSIENT_CFG_ELE		BIT(4)
6628e34278SMartin Fuzzey #define MMA8452_TRANSIENT_SRC			0x1e
6728e34278SMartin Fuzzey #define  MMA8452_TRANSIENT_SRC_XTRANSE		BIT(1)
6828e34278SMartin Fuzzey #define  MMA8452_TRANSIENT_SRC_YTRANSE		BIT(3)
6928e34278SMartin Fuzzey #define  MMA8452_TRANSIENT_SRC_ZTRANSE		BIT(5)
7028e34278SMartin Fuzzey #define MMA8452_TRANSIENT_THS			0x1f
7169abff81SHartmut Knaack #define  MMA8452_TRANSIENT_THS_MASK		GENMASK(6, 0)
725dbbd19fSMartin Fuzzey #define MMA8452_TRANSIENT_COUNT			0x20
73605f72deSHarinath Nampally #define MMA8452_TRANSIENT_CHAN_SHIFT		1
74c7eeea93SPeter Meerwald #define MMA8452_CTRL_REG1			0x2a
7569abff81SHartmut Knaack #define  MMA8452_CTRL_ACTIVE			BIT(0)
7669abff81SHartmut Knaack #define  MMA8452_CTRL_DR_MASK			GENMASK(5, 3)
7769abff81SHartmut Knaack #define  MMA8452_CTRL_DR_SHIFT			3
7869abff81SHartmut Knaack #define  MMA8452_CTRL_DR_DEFAULT		0x4 /* 50 Hz sample frequency */
79c7eeea93SPeter Meerwald #define MMA8452_CTRL_REG2			0x2b
80ecabae71SMartin Fuzzey #define  MMA8452_CTRL_REG2_RST			BIT(6)
81ed859fc1SMartin Kepplinger #define  MMA8452_CTRL_REG2_MODS_SHIFT		3
82ed859fc1SMartin Kepplinger #define  MMA8452_CTRL_REG2_MODS_MASK		0x1b
8328e34278SMartin Fuzzey #define MMA8452_CTRL_REG4			0x2d
8428e34278SMartin Fuzzey #define MMA8452_CTRL_REG5			0x2e
8569abff81SHartmut Knaack #define MMA8452_OFF_X				0x2f
8669abff81SHartmut Knaack #define MMA8452_OFF_Y				0x30
8769abff81SHartmut Knaack #define MMA8452_OFF_Z				0x31
88c7eeea93SPeter Meerwald 
892a17698cSMartin Fuzzey #define MMA8452_MAX_REG				0x31
902a17698cSMartin Fuzzey 
91ae6d9ce0SMartin Fuzzey #define  MMA8452_INT_DRDY			BIT(0)
9260f562e7SMartin Kepplinger #define  MMA8452_INT_FF_MT			BIT(2)
9328e34278SMartin Fuzzey #define  MMA8452_INT_TRANS			BIT(5)
9428e34278SMartin Fuzzey 
95244a93f6SMartin Kepplinger #define MMA8451_DEVICE_ID			0x1a
96c7eeea93SPeter Meerwald #define MMA8452_DEVICE_ID			0x2a
97c5ea1b58SMartin Kepplinger #define MMA8453_DEVICE_ID			0x3a
98417e008bSMartin Kepplinger #define MMA8652_DEVICE_ID			0x4a
99417e008bSMartin Kepplinger #define MMA8653_DEVICE_ID			0x5a
100e8731180SMartin Kepplinger #define FXLS8471_DEVICE_ID			0x6a
101c7eeea93SPeter Meerwald 
10296c0cb2bSMartin Kepplinger #define MMA8452_AUTO_SUSPEND_DELAY_MS		2000
10396c0cb2bSMartin Kepplinger 
104c7eeea93SPeter Meerwald struct mma8452_data {
105c7eeea93SPeter Meerwald 	struct i2c_client *client;
106c7eeea93SPeter Meerwald 	struct mutex lock;
107b863f2e3SHans de Goede 	struct iio_mount_matrix orientation;
108c7eeea93SPeter Meerwald 	u8 ctrl_reg1;
109c7eeea93SPeter Meerwald 	u8 data_cfg;
110c3cdd6e4SMartin Kepplinger 	const struct mma_chip_info *chip_info;
111a45d1238SRichard Tresidder 	int sleep_val;
112f6ff49b8SAnson Huang 	struct regulator *vdd_reg;
113f6ff49b8SAnson Huang 	struct regulator *vddio_reg;
11489226a29SJonathan Cameron 
11589226a29SJonathan Cameron 	/* Ensure correct alignment of time stamp when present */
11689226a29SJonathan Cameron 	struct {
11789226a29SJonathan Cameron 		__be16 channels[3];
11889226a29SJonathan Cameron 		s64 ts __aligned(8);
11989226a29SJonathan Cameron 	} buffer;
120c3cdd6e4SMartin Kepplinger };
121c3cdd6e4SMartin Kepplinger 
122c3cdd6e4SMartin Kepplinger  /**
123605f72deSHarinath Nampally   * struct mma8452_event_regs - chip specific data related to events
124c3cdd6e4SMartin Kepplinger   * @ev_cfg:			event config register address
125c3cdd6e4SMartin Kepplinger   * @ev_cfg_ele:			latch bit in event config register
126c3cdd6e4SMartin Kepplinger   * @ev_cfg_chan_shift:		number of the bit to enable events in X
127c3cdd6e4SMartin Kepplinger   *				direction; in event config register
128c3cdd6e4SMartin Kepplinger   * @ev_src:			event source register address
129c3cdd6e4SMartin Kepplinger   * @ev_ths:			event threshold register address
130c3cdd6e4SMartin Kepplinger   * @ev_ths_mask:		mask for the threshold value
131c3cdd6e4SMartin Kepplinger   * @ev_count:			event count (period) register address
132c3cdd6e4SMartin Kepplinger   *
133c3cdd6e4SMartin Kepplinger   * Since not all chips supported by the driver support comparing high pass
134c3cdd6e4SMartin Kepplinger   * filtered data for events (interrupts), different interrupt sources are
135c3cdd6e4SMartin Kepplinger   * used for different chips and the relevant registers are included here.
136c3cdd6e4SMartin Kepplinger   */
137605f72deSHarinath Nampally struct mma8452_event_regs {
138605f72deSHarinath Nampally 	u8 ev_cfg;
139605f72deSHarinath Nampally 	u8 ev_cfg_ele;
140605f72deSHarinath Nampally 	u8 ev_cfg_chan_shift;
141605f72deSHarinath Nampally 	u8 ev_src;
142605f72deSHarinath Nampally 	u8 ev_ths;
143605f72deSHarinath Nampally 	u8 ev_ths_mask;
144605f72deSHarinath Nampally 	u8 ev_count;
145605f72deSHarinath Nampally };
146605f72deSHarinath Nampally 
147a654c062SHarinath Nampally static const struct mma8452_event_regs ff_mt_ev_regs = {
148605f72deSHarinath Nampally 	.ev_cfg = MMA8452_FF_MT_CFG,
149605f72deSHarinath Nampally 	.ev_cfg_ele = MMA8452_FF_MT_CFG_ELE,
150605f72deSHarinath Nampally 	.ev_cfg_chan_shift = MMA8452_FF_MT_CHAN_SHIFT,
151605f72deSHarinath Nampally 	.ev_src = MMA8452_FF_MT_SRC,
152605f72deSHarinath Nampally 	.ev_ths = MMA8452_FF_MT_THS,
153605f72deSHarinath Nampally 	.ev_ths_mask = MMA8452_FF_MT_THS_MASK,
154605f72deSHarinath Nampally 	.ev_count = MMA8452_FF_MT_COUNT
155605f72deSHarinath Nampally };
156605f72deSHarinath Nampally 
157a654c062SHarinath Nampally static const struct mma8452_event_regs trans_ev_regs = {
158605f72deSHarinath Nampally 	.ev_cfg = MMA8452_TRANSIENT_CFG,
159605f72deSHarinath Nampally 	.ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE,
160605f72deSHarinath Nampally 	.ev_cfg_chan_shift = MMA8452_TRANSIENT_CHAN_SHIFT,
161605f72deSHarinath Nampally 	.ev_src = MMA8452_TRANSIENT_SRC,
162605f72deSHarinath Nampally 	.ev_ths = MMA8452_TRANSIENT_THS,
163605f72deSHarinath Nampally 	.ev_ths_mask = MMA8452_TRANSIENT_THS_MASK,
164605f72deSHarinath Nampally 	.ev_count = MMA8452_TRANSIENT_COUNT,
165605f72deSHarinath Nampally };
166605f72deSHarinath Nampally 
167605f72deSHarinath Nampally /**
168605f72deSHarinath Nampally  * struct mma_chip_info - chip specific data
169a503bc0aSJonathan Cameron  * @name:			part number of device reported via 'name' attr
170605f72deSHarinath Nampally  * @chip_id:			WHO_AM_I register's value
171605f72deSHarinath Nampally  * @channels:			struct iio_chan_spec matching the device's
172605f72deSHarinath Nampally  *				capabilities
173605f72deSHarinath Nampally  * @num_channels:		number of channels
174605f72deSHarinath Nampally  * @mma_scales:			scale factors for converting register values
175605f72deSHarinath Nampally  *				to m/s^2; 3 modes: 2g, 4g, 8g; 2 integers
176605f72deSHarinath Nampally  *				per mode: m/s^2 and micro m/s^2
177605f72deSHarinath Nampally  * @all_events:			all events supported by this chip
178605f72deSHarinath Nampally  * @enabled_events:		event flags enabled and handled by this driver
179605f72deSHarinath Nampally  */
180c3cdd6e4SMartin Kepplinger struct mma_chip_info {
181a47ac019SHans de Goede 	const char *name;
182c3cdd6e4SMartin Kepplinger 	u8 chip_id;
183c3cdd6e4SMartin Kepplinger 	const struct iio_chan_spec *channels;
184c3cdd6e4SMartin Kepplinger 	int num_channels;
185c3cdd6e4SMartin Kepplinger 	const int mma_scales[3][2];
186605f72deSHarinath Nampally 	int all_events;
187605f72deSHarinath Nampally 	int enabled_events;
188c7eeea93SPeter Meerwald };
189c7eeea93SPeter Meerwald 
190e60378c1SMartin Kepplinger enum {
191e60378c1SMartin Kepplinger 	idx_x,
192e60378c1SMartin Kepplinger 	idx_y,
193e60378c1SMartin Kepplinger 	idx_z,
194e60378c1SMartin Kepplinger 	idx_ts,
195e60378c1SMartin Kepplinger };
196e60378c1SMartin Kepplinger 
197c7eeea93SPeter Meerwald static int mma8452_drdy(struct mma8452_data *data)
198c7eeea93SPeter Meerwald {
199c7eeea93SPeter Meerwald 	int tries = 150;
200c7eeea93SPeter Meerwald 
201c7eeea93SPeter Meerwald 	while (tries-- > 0) {
202c7eeea93SPeter Meerwald 		int ret = i2c_smbus_read_byte_data(data->client,
203c7eeea93SPeter Meerwald 			MMA8452_STATUS);
204c7eeea93SPeter Meerwald 		if (ret < 0)
205c7eeea93SPeter Meerwald 			return ret;
206c7eeea93SPeter Meerwald 		if ((ret & MMA8452_STATUS_DRDY) == MMA8452_STATUS_DRDY)
207c7eeea93SPeter Meerwald 			return 0;
208686027fbSHartmut Knaack 
209a45d1238SRichard Tresidder 		if (data->sleep_val <= 20)
210a45d1238SRichard Tresidder 			usleep_range(data->sleep_val * 250,
211a45d1238SRichard Tresidder 				     data->sleep_val * 500);
212a45d1238SRichard Tresidder 		else
213c7eeea93SPeter Meerwald 			msleep(20);
214c7eeea93SPeter Meerwald 	}
215c7eeea93SPeter Meerwald 
216c7eeea93SPeter Meerwald 	dev_err(&data->client->dev, "data not ready\n");
217686027fbSHartmut Knaack 
218c7eeea93SPeter Meerwald 	return -EIO;
219c7eeea93SPeter Meerwald }
220c7eeea93SPeter Meerwald 
22196c0cb2bSMartin Kepplinger static int mma8452_set_runtime_pm_state(struct i2c_client *client, bool on)
22296c0cb2bSMartin Kepplinger {
22396c0cb2bSMartin Kepplinger #ifdef CONFIG_PM
22496c0cb2bSMartin Kepplinger 	int ret;
22596c0cb2bSMartin Kepplinger 
22696c0cb2bSMartin Kepplinger 	if (on) {
2275937b860SJonathan Cameron 		ret = pm_runtime_resume_and_get(&client->dev);
22896c0cb2bSMartin Kepplinger 	} else {
22996c0cb2bSMartin Kepplinger 		pm_runtime_mark_last_busy(&client->dev);
23096c0cb2bSMartin Kepplinger 		ret = pm_runtime_put_autosuspend(&client->dev);
23196c0cb2bSMartin Kepplinger 	}
23296c0cb2bSMartin Kepplinger 
23396c0cb2bSMartin Kepplinger 	if (ret < 0) {
23496c0cb2bSMartin Kepplinger 		dev_err(&client->dev,
23596c0cb2bSMartin Kepplinger 			"failed to change power state to %d\n", on);
23696c0cb2bSMartin Kepplinger 
23796c0cb2bSMartin Kepplinger 		return ret;
23896c0cb2bSMartin Kepplinger 	}
23996c0cb2bSMartin Kepplinger #endif
24096c0cb2bSMartin Kepplinger 
24196c0cb2bSMartin Kepplinger 	return 0;
24296c0cb2bSMartin Kepplinger }
24396c0cb2bSMartin Kepplinger 
244c7eeea93SPeter Meerwald static int mma8452_read(struct mma8452_data *data, __be16 buf[3])
245c7eeea93SPeter Meerwald {
246c7eeea93SPeter Meerwald 	int ret = mma8452_drdy(data);
247686027fbSHartmut Knaack 
248c7eeea93SPeter Meerwald 	if (ret < 0)
249c7eeea93SPeter Meerwald 		return ret;
250686027fbSHartmut Knaack 
25196c0cb2bSMartin Kepplinger 	ret = mma8452_set_runtime_pm_state(data->client, true);
25296c0cb2bSMartin Kepplinger 	if (ret)
25396c0cb2bSMartin Kepplinger 		return ret;
25496c0cb2bSMartin Kepplinger 
25596c0cb2bSMartin Kepplinger 	ret = i2c_smbus_read_i2c_block_data(data->client, MMA8452_OUT_X,
256686027fbSHartmut Knaack 					    3 * sizeof(__be16), (u8 *)buf);
25796c0cb2bSMartin Kepplinger 
25896c0cb2bSMartin Kepplinger 	ret = mma8452_set_runtime_pm_state(data->client, false);
25996c0cb2bSMartin Kepplinger 
26096c0cb2bSMartin Kepplinger 	return ret;
261c7eeea93SPeter Meerwald }
262c7eeea93SPeter Meerwald 
263686027fbSHartmut Knaack static ssize_t mma8452_show_int_plus_micros(char *buf, const int (*vals)[2],
264686027fbSHartmut Knaack 					    int n)
265c7eeea93SPeter Meerwald {
266c7eeea93SPeter Meerwald 	size_t len = 0;
267c7eeea93SPeter Meerwald 
268c7eeea93SPeter Meerwald 	while (n-- > 0)
269686027fbSHartmut Knaack 		len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ",
270686027fbSHartmut Knaack 				 vals[n][0], vals[n][1]);
271c7eeea93SPeter Meerwald 
272c7eeea93SPeter Meerwald 	/* replace trailing space by newline */
273c7eeea93SPeter Meerwald 	buf[len - 1] = '\n';
274c7eeea93SPeter Meerwald 
275c7eeea93SPeter Meerwald 	return len;
276c7eeea93SPeter Meerwald }
277c7eeea93SPeter Meerwald 
278c7eeea93SPeter Meerwald static int mma8452_get_int_plus_micros_index(const int (*vals)[2], int n,
279c7eeea93SPeter Meerwald 					     int val, int val2)
280c7eeea93SPeter Meerwald {
281c7eeea93SPeter Meerwald 	while (n-- > 0)
282c7eeea93SPeter Meerwald 		if (val == vals[n][0] && val2 == vals[n][1])
283c7eeea93SPeter Meerwald 			return n;
284c7eeea93SPeter Meerwald 
285c7eeea93SPeter Meerwald 	return -EINVAL;
286c7eeea93SPeter Meerwald }
287c7eeea93SPeter Meerwald 
28832b28076SMartin Kepplinger static unsigned int mma8452_get_odr_index(struct mma8452_data *data)
2895dbbd19fSMartin Fuzzey {
2905dbbd19fSMartin Fuzzey 	return (data->ctrl_reg1 & MMA8452_CTRL_DR_MASK) >>
2915dbbd19fSMartin Fuzzey 			MMA8452_CTRL_DR_SHIFT;
2925dbbd19fSMartin Fuzzey }
2935dbbd19fSMartin Fuzzey 
294c7eeea93SPeter Meerwald static const int mma8452_samp_freq[8][2] = {
295c7eeea93SPeter Meerwald 	{800, 0}, {400, 0}, {200, 0}, {100, 0}, {50, 0}, {12, 500000},
296c7eeea93SPeter Meerwald 	{6, 250000}, {1, 560000}
297c7eeea93SPeter Meerwald };
298c7eeea93SPeter Meerwald 
299ed859fc1SMartin Kepplinger /* Datasheet table: step time "Relationship with the ODR" (sample frequency) */
300cc54a660SHarinath Nampally static const unsigned int mma8452_time_step_us[4][8] = {
301ed859fc1SMartin Kepplinger 	{ 1250, 2500, 5000, 10000, 20000, 20000, 20000, 20000 },  /* normal */
302ed859fc1SMartin Kepplinger 	{ 1250, 2500, 5000, 10000, 20000, 80000, 80000, 80000 },  /* l p l n */
303ed859fc1SMartin Kepplinger 	{ 1250, 2500, 2500, 2500, 2500, 2500, 2500, 2500 },	  /* high res*/
304ed859fc1SMartin Kepplinger 	{ 1250, 2500, 5000, 10000, 20000, 80000, 160000, 160000 } /* l p */
3055dbbd19fSMartin Fuzzey };
3065dbbd19fSMartin Fuzzey 
307ed859fc1SMartin Kepplinger /* Datasheet table "High-Pass Filter Cutoff Options" */
308ed859fc1SMartin Kepplinger static const int mma8452_hp_filter_cutoff[4][8][4][2] = {
309ed859fc1SMartin Kepplinger 	{ /* normal */
3101e79841aSMartin Fuzzey 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },		/* 800 Hz sample */
3111e79841aSMartin Fuzzey 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },		/* 400 Hz sample */
3121e79841aSMartin Fuzzey 	{ {8, 0}, {4, 0}, {2, 0}, {1, 0} },		/* 200 Hz sample */
3131e79841aSMartin Fuzzey 	{ {4, 0}, {2, 0}, {1, 0}, {0, 500000} },	/* 100 Hz sample */
3141e79841aSMartin Fuzzey 	{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} },	/* 50 Hz sample */
3151e79841aSMartin Fuzzey 	{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} },	/* 12.5 Hz sample */
3161e79841aSMartin Fuzzey 	{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} },	/* 6.25 Hz sample */
3171e79841aSMartin Fuzzey 	{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }	/* 1.56 Hz sample */
318ed859fc1SMartin Kepplinger 	},
319ed859fc1SMartin Kepplinger 	{ /* low noise low power */
320ed859fc1SMartin Kepplinger 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
321ed859fc1SMartin Kepplinger 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
322ed859fc1SMartin Kepplinger 	{ {8, 0}, {4, 0}, {2, 0}, {1, 0} },
323ed859fc1SMartin Kepplinger 	{ {4, 0}, {2, 0}, {1, 0}, {0, 500000} },
324ed859fc1SMartin Kepplinger 	{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} },
325ed859fc1SMartin Kepplinger 	{ {0, 500000}, {0, 250000}, {0, 125000}, {0, 063000} },
326ed859fc1SMartin Kepplinger 	{ {0, 500000}, {0, 250000}, {0, 125000}, {0, 063000} },
327ed859fc1SMartin Kepplinger 	{ {0, 500000}, {0, 250000}, {0, 125000}, {0, 063000} }
328ed859fc1SMartin Kepplinger 	},
329ed859fc1SMartin Kepplinger 	{ /* high resolution */
330ed859fc1SMartin Kepplinger 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
331ed859fc1SMartin Kepplinger 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
332ed859fc1SMartin Kepplinger 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
333ed859fc1SMartin Kepplinger 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
334ed859fc1SMartin Kepplinger 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
335ed859fc1SMartin Kepplinger 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
336ed859fc1SMartin Kepplinger 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
337ed859fc1SMartin Kepplinger 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} }
338ed859fc1SMartin Kepplinger 	},
339ed859fc1SMartin Kepplinger 	{ /* low power */
340ed859fc1SMartin Kepplinger 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
341ed859fc1SMartin Kepplinger 	{ {8, 0}, {4, 0}, {2, 0}, {1, 0} },
342ed859fc1SMartin Kepplinger 	{ {4, 0}, {2, 0}, {1, 0}, {0, 500000} },
343ed859fc1SMartin Kepplinger 	{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} },
344ed859fc1SMartin Kepplinger 	{ {1, 0}, {0, 500000}, {0, 250000}, {0, 125000} },
345ed859fc1SMartin Kepplinger 	{ {0, 250000}, {0, 125000}, {0, 063000}, {0, 031000} },
346ed859fc1SMartin Kepplinger 	{ {0, 250000}, {0, 125000}, {0, 063000}, {0, 031000} },
347ed859fc1SMartin Kepplinger 	{ {0, 250000}, {0, 125000}, {0, 063000}, {0, 031000} }
348ed859fc1SMartin Kepplinger 	}
3491e79841aSMartin Fuzzey };
3501e79841aSMartin Fuzzey 
351ed859fc1SMartin Kepplinger /* Datasheet table "MODS Oversampling modes averaging values at each ODR" */
352ed859fc1SMartin Kepplinger static const u16 mma8452_os_ratio[4][8] = {
353ed859fc1SMartin Kepplinger 	/* 800 Hz, 400 Hz, ... , 1.56 Hz */
354ed859fc1SMartin Kepplinger 	{ 2, 4, 4, 4, 4, 16, 32, 128 },		/* normal */
355ed859fc1SMartin Kepplinger 	{ 2, 4, 4, 4, 4, 4, 8, 32 },		/* low power low noise */
356ed859fc1SMartin Kepplinger 	{ 2, 4, 8, 16, 32, 128, 256, 1024 },	/* high resolution */
357ed859fc1SMartin Kepplinger 	{ 2, 2, 2, 2, 2, 2, 4, 16 }		/* low power */
358ed859fc1SMartin Kepplinger };
359ed859fc1SMartin Kepplinger 
360ed859fc1SMartin Kepplinger static int mma8452_get_power_mode(struct mma8452_data *data)
361ed859fc1SMartin Kepplinger {
362ed859fc1SMartin Kepplinger 	int reg;
363ed859fc1SMartin Kepplinger 
364ed859fc1SMartin Kepplinger 	reg = i2c_smbus_read_byte_data(data->client,
365ed859fc1SMartin Kepplinger 				       MMA8452_CTRL_REG2);
366ed859fc1SMartin Kepplinger 	if (reg < 0)
367ed859fc1SMartin Kepplinger 		return reg;
368ed859fc1SMartin Kepplinger 
369ed859fc1SMartin Kepplinger 	return ((reg & MMA8452_CTRL_REG2_MODS_MASK) >>
370ed859fc1SMartin Kepplinger 		MMA8452_CTRL_REG2_MODS_SHIFT);
371ed859fc1SMartin Kepplinger }
372ed859fc1SMartin Kepplinger 
373c7eeea93SPeter Meerwald static ssize_t mma8452_show_samp_freq_avail(struct device *dev,
374686027fbSHartmut Knaack 					    struct device_attribute *attr,
375686027fbSHartmut Knaack 					    char *buf)
376c7eeea93SPeter Meerwald {
377c7eeea93SPeter Meerwald 	return mma8452_show_int_plus_micros(buf, mma8452_samp_freq,
378c7eeea93SPeter Meerwald 					    ARRAY_SIZE(mma8452_samp_freq));
379c7eeea93SPeter Meerwald }
380c7eeea93SPeter Meerwald 
381c7eeea93SPeter Meerwald static ssize_t mma8452_show_scale_avail(struct device *dev,
382686027fbSHartmut Knaack 					struct device_attribute *attr,
383686027fbSHartmut Knaack 					char *buf)
384c7eeea93SPeter Meerwald {
385c87b7b12SHaibo Chen 	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
386c87b7b12SHaibo Chen 	struct mma8452_data *data = iio_priv(indio_dev);
387c3cdd6e4SMartin Kepplinger 
388c3cdd6e4SMartin Kepplinger 	return mma8452_show_int_plus_micros(buf, data->chip_info->mma_scales,
389c3cdd6e4SMartin Kepplinger 		ARRAY_SIZE(data->chip_info->mma_scales));
390c7eeea93SPeter Meerwald }
391c7eeea93SPeter Meerwald 
3921e79841aSMartin Fuzzey static ssize_t mma8452_show_hp_cutoff_avail(struct device *dev,
3931e79841aSMartin Fuzzey 					    struct device_attribute *attr,
3941e79841aSMartin Fuzzey 					    char *buf)
3951e79841aSMartin Fuzzey {
3961e79841aSMartin Fuzzey 	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
3971e79841aSMartin Fuzzey 	struct mma8452_data *data = iio_priv(indio_dev);
398ed859fc1SMartin Kepplinger 	int i, j;
3991e79841aSMartin Fuzzey 
400ed859fc1SMartin Kepplinger 	i = mma8452_get_odr_index(data);
401ed859fc1SMartin Kepplinger 	j = mma8452_get_power_mode(data);
402ed859fc1SMartin Kepplinger 	if (j < 0)
403ed859fc1SMartin Kepplinger 		return j;
404ed859fc1SMartin Kepplinger 
405ed859fc1SMartin Kepplinger 	return mma8452_show_int_plus_micros(buf, mma8452_hp_filter_cutoff[j][i],
406ed859fc1SMartin Kepplinger 		ARRAY_SIZE(mma8452_hp_filter_cutoff[0][0]));
407ed859fc1SMartin Kepplinger }
408ed859fc1SMartin Kepplinger 
409ed859fc1SMartin Kepplinger static ssize_t mma8452_show_os_ratio_avail(struct device *dev,
410ed859fc1SMartin Kepplinger 					   struct device_attribute *attr,
411ed859fc1SMartin Kepplinger 					   char *buf)
412ed859fc1SMartin Kepplinger {
413ed859fc1SMartin Kepplinger 	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
414ed859fc1SMartin Kepplinger 	struct mma8452_data *data = iio_priv(indio_dev);
415ed859fc1SMartin Kepplinger 	int i = mma8452_get_odr_index(data);
416ed859fc1SMartin Kepplinger 	int j;
417ed859fc1SMartin Kepplinger 	u16 val = 0;
418ed859fc1SMartin Kepplinger 	size_t len = 0;
419ed859fc1SMartin Kepplinger 
420ed859fc1SMartin Kepplinger 	for (j = 0; j < ARRAY_SIZE(mma8452_os_ratio); j++) {
421ed859fc1SMartin Kepplinger 		if (val == mma8452_os_ratio[j][i])
422ed859fc1SMartin Kepplinger 			continue;
423ed859fc1SMartin Kepplinger 
424ed859fc1SMartin Kepplinger 		val = mma8452_os_ratio[j][i];
425ed859fc1SMartin Kepplinger 
426ed859fc1SMartin Kepplinger 		len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", val);
427ed859fc1SMartin Kepplinger 	}
428ed859fc1SMartin Kepplinger 	buf[len - 1] = '\n';
429ed859fc1SMartin Kepplinger 
430ed859fc1SMartin Kepplinger 	return len;
4311e79841aSMartin Fuzzey }
4321e79841aSMartin Fuzzey 
433c7eeea93SPeter Meerwald static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mma8452_show_samp_freq_avail);
434cd327b00SHarinath Nampally static IIO_DEVICE_ATTR(in_accel_scale_available, 0444,
435c7eeea93SPeter Meerwald 		       mma8452_show_scale_avail, NULL, 0);
4361e79841aSMartin Fuzzey static IIO_DEVICE_ATTR(in_accel_filter_high_pass_3db_frequency_available,
437cd327b00SHarinath Nampally 		       0444, mma8452_show_hp_cutoff_avail, NULL, 0);
438cd327b00SHarinath Nampally static IIO_DEVICE_ATTR(in_accel_oversampling_ratio_available, 0444,
439ed859fc1SMartin Kepplinger 		       mma8452_show_os_ratio_avail, NULL, 0);
440c7eeea93SPeter Meerwald 
441c7eeea93SPeter Meerwald static int mma8452_get_samp_freq_index(struct mma8452_data *data,
442c7eeea93SPeter Meerwald 				       int val, int val2)
443c7eeea93SPeter Meerwald {
444c7eeea93SPeter Meerwald 	return mma8452_get_int_plus_micros_index(mma8452_samp_freq,
445686027fbSHartmut Knaack 						 ARRAY_SIZE(mma8452_samp_freq),
446686027fbSHartmut Knaack 						 val, val2);
447c7eeea93SPeter Meerwald }
448c7eeea93SPeter Meerwald 
449686027fbSHartmut Knaack static int mma8452_get_scale_index(struct mma8452_data *data, int val, int val2)
450c7eeea93SPeter Meerwald {
451c3cdd6e4SMartin Kepplinger 	return mma8452_get_int_plus_micros_index(data->chip_info->mma_scales,
452c3cdd6e4SMartin Kepplinger 			ARRAY_SIZE(data->chip_info->mma_scales), val, val2);
453c7eeea93SPeter Meerwald }
454c7eeea93SPeter Meerwald 
4551e79841aSMartin Fuzzey static int mma8452_get_hp_filter_index(struct mma8452_data *data,
4561e79841aSMartin Fuzzey 				       int val, int val2)
4571e79841aSMartin Fuzzey {
458ed859fc1SMartin Kepplinger 	int i, j;
4591e79841aSMartin Fuzzey 
460ed859fc1SMartin Kepplinger 	i = mma8452_get_odr_index(data);
461ed859fc1SMartin Kepplinger 	j = mma8452_get_power_mode(data);
462ed859fc1SMartin Kepplinger 	if (j < 0)
463ed859fc1SMartin Kepplinger 		return j;
464ed859fc1SMartin Kepplinger 
465ed859fc1SMartin Kepplinger 	return mma8452_get_int_plus_micros_index(mma8452_hp_filter_cutoff[j][i],
466ed859fc1SMartin Kepplinger 		ARRAY_SIZE(mma8452_hp_filter_cutoff[0][0]), val, val2);
4671e79841aSMartin Fuzzey }
4681e79841aSMartin Fuzzey 
4691e79841aSMartin Fuzzey static int mma8452_read_hp_filter(struct mma8452_data *data, int *hz, int *uHz)
4701e79841aSMartin Fuzzey {
471ed859fc1SMartin Kepplinger 	int j, i, ret;
4721e79841aSMartin Fuzzey 
4731e79841aSMartin Fuzzey 	ret = i2c_smbus_read_byte_data(data->client, MMA8452_HP_FILTER_CUTOFF);
4741e79841aSMartin Fuzzey 	if (ret < 0)
4751e79841aSMartin Fuzzey 		return ret;
4761e79841aSMartin Fuzzey 
4771e79841aSMartin Fuzzey 	i = mma8452_get_odr_index(data);
478ed859fc1SMartin Kepplinger 	j = mma8452_get_power_mode(data);
479ed859fc1SMartin Kepplinger 	if (j < 0)
480ed859fc1SMartin Kepplinger 		return j;
481ed859fc1SMartin Kepplinger 
4821e79841aSMartin Fuzzey 	ret &= MMA8452_HP_FILTER_CUTOFF_SEL_MASK;
483ed859fc1SMartin Kepplinger 	*hz = mma8452_hp_filter_cutoff[j][i][ret][0];
484ed859fc1SMartin Kepplinger 	*uHz = mma8452_hp_filter_cutoff[j][i][ret][1];
4851e79841aSMartin Fuzzey 
4861e79841aSMartin Fuzzey 	return 0;
4871e79841aSMartin Fuzzey }
4881e79841aSMartin Fuzzey 
489c7eeea93SPeter Meerwald static int mma8452_read_raw(struct iio_dev *indio_dev,
490c7eeea93SPeter Meerwald 			    struct iio_chan_spec const *chan,
491c7eeea93SPeter Meerwald 			    int *val, int *val2, long mask)
492c7eeea93SPeter Meerwald {
493c7eeea93SPeter Meerwald 	struct mma8452_data *data = iio_priv(indio_dev);
494c7eeea93SPeter Meerwald 	__be16 buffer[3];
495c7eeea93SPeter Meerwald 	int i, ret;
496c7eeea93SPeter Meerwald 
497c7eeea93SPeter Meerwald 	switch (mask) {
498c7eeea93SPeter Meerwald 	case IIO_CHAN_INFO_RAW:
4994d9b0413SAlison Schofield 		ret = iio_device_claim_direct_mode(indio_dev);
5004d9b0413SAlison Schofield 		if (ret)
5014d9b0413SAlison Schofield 			return ret;
502c7eeea93SPeter Meerwald 
503c7eeea93SPeter Meerwald 		mutex_lock(&data->lock);
504c7eeea93SPeter Meerwald 		ret = mma8452_read(data, buffer);
505c7eeea93SPeter Meerwald 		mutex_unlock(&data->lock);
5064d9b0413SAlison Schofield 		iio_device_release_direct_mode(indio_dev);
507c7eeea93SPeter Meerwald 		if (ret < 0)
508c7eeea93SPeter Meerwald 			return ret;
509686027fbSHartmut Knaack 
510c3cdd6e4SMartin Kepplinger 		*val = sign_extend32(be16_to_cpu(
511c3cdd6e4SMartin Kepplinger 			buffer[chan->scan_index]) >> chan->scan_type.shift,
512c3cdd6e4SMartin Kepplinger 			chan->scan_type.realbits - 1);
513686027fbSHartmut Knaack 
514c7eeea93SPeter Meerwald 		return IIO_VAL_INT;
515c7eeea93SPeter Meerwald 	case IIO_CHAN_INFO_SCALE:
516c7eeea93SPeter Meerwald 		i = data->data_cfg & MMA8452_DATA_CFG_FS_MASK;
517c3cdd6e4SMartin Kepplinger 		*val = data->chip_info->mma_scales[i][0];
518c3cdd6e4SMartin Kepplinger 		*val2 = data->chip_info->mma_scales[i][1];
519686027fbSHartmut Knaack 
520c7eeea93SPeter Meerwald 		return IIO_VAL_INT_PLUS_MICRO;
521c7eeea93SPeter Meerwald 	case IIO_CHAN_INFO_SAMP_FREQ:
5225dbbd19fSMartin Fuzzey 		i = mma8452_get_odr_index(data);
523c7eeea93SPeter Meerwald 		*val = mma8452_samp_freq[i][0];
524c7eeea93SPeter Meerwald 		*val2 = mma8452_samp_freq[i][1];
525686027fbSHartmut Knaack 
526c7eeea93SPeter Meerwald 		return IIO_VAL_INT_PLUS_MICRO;
527c7eeea93SPeter Meerwald 	case IIO_CHAN_INFO_CALIBBIAS:
528686027fbSHartmut Knaack 		ret = i2c_smbus_read_byte_data(data->client,
5298b8ff3a6SMartin Kepplinger 					       MMA8452_OFF_X +
5308b8ff3a6SMartin Kepplinger 					       chan->scan_index);
531c7eeea93SPeter Meerwald 		if (ret < 0)
532c7eeea93SPeter Meerwald 			return ret;
533686027fbSHartmut Knaack 
534c7eeea93SPeter Meerwald 		*val = sign_extend32(ret, 7);
535686027fbSHartmut Knaack 
536c7eeea93SPeter Meerwald 		return IIO_VAL_INT;
5371e79841aSMartin Fuzzey 	case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
5381e79841aSMartin Fuzzey 		if (data->data_cfg & MMA8452_DATA_CFG_HPF_MASK) {
5391e79841aSMartin Fuzzey 			ret = mma8452_read_hp_filter(data, val, val2);
5401e79841aSMartin Fuzzey 			if (ret < 0)
5411e79841aSMartin Fuzzey 				return ret;
5421e79841aSMartin Fuzzey 		} else {
5431e79841aSMartin Fuzzey 			*val = 0;
5441e79841aSMartin Fuzzey 			*val2 = 0;
5451e79841aSMartin Fuzzey 		}
546686027fbSHartmut Knaack 
5471e79841aSMartin Fuzzey 		return IIO_VAL_INT_PLUS_MICRO;
548ed859fc1SMartin Kepplinger 	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
549ed859fc1SMartin Kepplinger 		ret = mma8452_get_power_mode(data);
550ed859fc1SMartin Kepplinger 		if (ret < 0)
551ed859fc1SMartin Kepplinger 			return ret;
552ed859fc1SMartin Kepplinger 
553ed859fc1SMartin Kepplinger 		i = mma8452_get_odr_index(data);
554ed859fc1SMartin Kepplinger 
555ed859fc1SMartin Kepplinger 		*val = mma8452_os_ratio[ret][i];
556ed859fc1SMartin Kepplinger 		return IIO_VAL_INT;
557c7eeea93SPeter Meerwald 	}
558686027fbSHartmut Knaack 
559c7eeea93SPeter Meerwald 	return -EINVAL;
560c7eeea93SPeter Meerwald }
561c7eeea93SPeter Meerwald 
562a45d1238SRichard Tresidder static int mma8452_calculate_sleep(struct mma8452_data *data)
563a45d1238SRichard Tresidder {
564a45d1238SRichard Tresidder 	int ret, i = mma8452_get_odr_index(data);
565a45d1238SRichard Tresidder 
566a45d1238SRichard Tresidder 	if (mma8452_samp_freq[i][0] > 0)
567a45d1238SRichard Tresidder 		ret = 1000 / mma8452_samp_freq[i][0];
568a45d1238SRichard Tresidder 	else
569a45d1238SRichard Tresidder 		ret = 1000;
570a45d1238SRichard Tresidder 
571a45d1238SRichard Tresidder 	return ret == 0 ? 1 : ret;
572a45d1238SRichard Tresidder }
573a45d1238SRichard Tresidder 
574c7eeea93SPeter Meerwald static int mma8452_standby(struct mma8452_data *data)
575c7eeea93SPeter Meerwald {
576c7eeea93SPeter Meerwald 	return i2c_smbus_write_byte_data(data->client, MMA8452_CTRL_REG1,
577c7eeea93SPeter Meerwald 					data->ctrl_reg1 & ~MMA8452_CTRL_ACTIVE);
578c7eeea93SPeter Meerwald }
579c7eeea93SPeter Meerwald 
580c7eeea93SPeter Meerwald static int mma8452_active(struct mma8452_data *data)
581c7eeea93SPeter Meerwald {
582c7eeea93SPeter Meerwald 	return i2c_smbus_write_byte_data(data->client, MMA8452_CTRL_REG1,
583c7eeea93SPeter Meerwald 					 data->ctrl_reg1);
584c7eeea93SPeter Meerwald }
585c7eeea93SPeter Meerwald 
586e866853dSMartin Kepplinger /* returns >0 if active, 0 if in standby and <0 on error */
587e866853dSMartin Kepplinger static int mma8452_is_active(struct mma8452_data *data)
588e866853dSMartin Kepplinger {
589e866853dSMartin Kepplinger 	int reg;
590e866853dSMartin Kepplinger 
591e866853dSMartin Kepplinger 	reg = i2c_smbus_read_byte_data(data->client, MMA8452_CTRL_REG1);
592e866853dSMartin Kepplinger 	if (reg < 0)
593e866853dSMartin Kepplinger 		return reg;
594e866853dSMartin Kepplinger 
595e866853dSMartin Kepplinger 	return reg & MMA8452_CTRL_ACTIVE;
596e866853dSMartin Kepplinger }
597e866853dSMartin Kepplinger 
598c7eeea93SPeter Meerwald static int mma8452_change_config(struct mma8452_data *data, u8 reg, u8 val)
599c7eeea93SPeter Meerwald {
600c7eeea93SPeter Meerwald 	int ret;
601e866853dSMartin Kepplinger 	int is_active;
602c7eeea93SPeter Meerwald 
603c7eeea93SPeter Meerwald 	mutex_lock(&data->lock);
604c7eeea93SPeter Meerwald 
605e866853dSMartin Kepplinger 	is_active = mma8452_is_active(data);
606e866853dSMartin Kepplinger 	if (is_active < 0) {
607e866853dSMartin Kepplinger 		ret = is_active;
608e866853dSMartin Kepplinger 		goto fail;
609e866853dSMartin Kepplinger 	}
610e866853dSMartin Kepplinger 
611c7eeea93SPeter Meerwald 	/* config can only be changed when in standby */
612e866853dSMartin Kepplinger 	if (is_active > 0) {
613c7eeea93SPeter Meerwald 		ret = mma8452_standby(data);
614c7eeea93SPeter Meerwald 		if (ret < 0)
615c7eeea93SPeter Meerwald 			goto fail;
616e866853dSMartin Kepplinger 	}
617c7eeea93SPeter Meerwald 
618c7eeea93SPeter Meerwald 	ret = i2c_smbus_write_byte_data(data->client, reg, val);
619c7eeea93SPeter Meerwald 	if (ret < 0)
620c7eeea93SPeter Meerwald 		goto fail;
621c7eeea93SPeter Meerwald 
622e866853dSMartin Kepplinger 	if (is_active > 0) {
623c7eeea93SPeter Meerwald 		ret = mma8452_active(data);
624c7eeea93SPeter Meerwald 		if (ret < 0)
625c7eeea93SPeter Meerwald 			goto fail;
626e866853dSMartin Kepplinger 	}
627c7eeea93SPeter Meerwald 
628c7eeea93SPeter Meerwald 	ret = 0;
629c7eeea93SPeter Meerwald fail:
630c7eeea93SPeter Meerwald 	mutex_unlock(&data->lock);
631686027fbSHartmut Knaack 
632c7eeea93SPeter Meerwald 	return ret;
633c7eeea93SPeter Meerwald }
634c7eeea93SPeter Meerwald 
635ed859fc1SMartin Kepplinger static int mma8452_set_power_mode(struct mma8452_data *data, u8 mode)
636ed859fc1SMartin Kepplinger {
637ed859fc1SMartin Kepplinger 	int reg;
638ed859fc1SMartin Kepplinger 
639ed859fc1SMartin Kepplinger 	reg = i2c_smbus_read_byte_data(data->client,
640ed859fc1SMartin Kepplinger 				       MMA8452_CTRL_REG2);
641ed859fc1SMartin Kepplinger 	if (reg < 0)
642ed859fc1SMartin Kepplinger 		return reg;
643ed859fc1SMartin Kepplinger 
644ed859fc1SMartin Kepplinger 	reg &= ~MMA8452_CTRL_REG2_MODS_MASK;
645ed859fc1SMartin Kepplinger 	reg |= mode << MMA8452_CTRL_REG2_MODS_SHIFT;
646ed859fc1SMartin Kepplinger 
647ed859fc1SMartin Kepplinger 	return mma8452_change_config(data, MMA8452_CTRL_REG2, reg);
648ed859fc1SMartin Kepplinger }
649ed859fc1SMartin Kepplinger 
6508b8ff3a6SMartin Kepplinger /* returns >0 if in freefall mode, 0 if not or <0 if an error occurred */
6514b04266aSMartin Kepplinger static int mma8452_freefall_mode_enabled(struct mma8452_data *data)
6524b04266aSMartin Kepplinger {
6534b04266aSMartin Kepplinger 	int val;
6544b04266aSMartin Kepplinger 
655605f72deSHarinath Nampally 	val = i2c_smbus_read_byte_data(data->client, MMA8452_FF_MT_CFG);
6564b04266aSMartin Kepplinger 	if (val < 0)
6574b04266aSMartin Kepplinger 		return val;
6584b04266aSMartin Kepplinger 
6594b04266aSMartin Kepplinger 	return !(val & MMA8452_FF_MT_CFG_OAE);
6604b04266aSMartin Kepplinger }
6614b04266aSMartin Kepplinger 
6624b04266aSMartin Kepplinger static int mma8452_set_freefall_mode(struct mma8452_data *data, bool state)
6634b04266aSMartin Kepplinger {
6644b04266aSMartin Kepplinger 	int val;
6654b04266aSMartin Kepplinger 
6664b04266aSMartin Kepplinger 	if ((state && mma8452_freefall_mode_enabled(data)) ||
6674b04266aSMartin Kepplinger 	    (!state && !(mma8452_freefall_mode_enabled(data))))
6684b04266aSMartin Kepplinger 		return 0;
6694b04266aSMartin Kepplinger 
670605f72deSHarinath Nampally 	val = i2c_smbus_read_byte_data(data->client, MMA8452_FF_MT_CFG);
6714b04266aSMartin Kepplinger 	if (val < 0)
6724b04266aSMartin Kepplinger 		return val;
6734b04266aSMartin Kepplinger 
6744b04266aSMartin Kepplinger 	if (state) {
675605f72deSHarinath Nampally 		val |= BIT(idx_x + MMA8452_FF_MT_CHAN_SHIFT);
676605f72deSHarinath Nampally 		val |= BIT(idx_y + MMA8452_FF_MT_CHAN_SHIFT);
677605f72deSHarinath Nampally 		val |= BIT(idx_z + MMA8452_FF_MT_CHAN_SHIFT);
6784b04266aSMartin Kepplinger 		val &= ~MMA8452_FF_MT_CFG_OAE;
6794b04266aSMartin Kepplinger 	} else {
680605f72deSHarinath Nampally 		val &= ~BIT(idx_x + MMA8452_FF_MT_CHAN_SHIFT);
681605f72deSHarinath Nampally 		val &= ~BIT(idx_y + MMA8452_FF_MT_CHAN_SHIFT);
682605f72deSHarinath Nampally 		val &= ~BIT(idx_z + MMA8452_FF_MT_CHAN_SHIFT);
6834b04266aSMartin Kepplinger 		val |= MMA8452_FF_MT_CFG_OAE;
6844b04266aSMartin Kepplinger 	}
6854b04266aSMartin Kepplinger 
686605f72deSHarinath Nampally 	return mma8452_change_config(data, MMA8452_FF_MT_CFG, val);
6874b04266aSMartin Kepplinger }
6884b04266aSMartin Kepplinger 
6891e79841aSMartin Fuzzey static int mma8452_set_hp_filter_frequency(struct mma8452_data *data,
6901e79841aSMartin Fuzzey 					   int val, int val2)
6911e79841aSMartin Fuzzey {
6921e79841aSMartin Fuzzey 	int i, reg;
6931e79841aSMartin Fuzzey 
6941e79841aSMartin Fuzzey 	i = mma8452_get_hp_filter_index(data, val, val2);
6951e79841aSMartin Fuzzey 	if (i < 0)
696b9fddcdbSHartmut Knaack 		return i;
6971e79841aSMartin Fuzzey 
6981e79841aSMartin Fuzzey 	reg = i2c_smbus_read_byte_data(data->client,
6991e79841aSMartin Fuzzey 				       MMA8452_HP_FILTER_CUTOFF);
7001e79841aSMartin Fuzzey 	if (reg < 0)
7011e79841aSMartin Fuzzey 		return reg;
702686027fbSHartmut Knaack 
7031e79841aSMartin Fuzzey 	reg &= ~MMA8452_HP_FILTER_CUTOFF_SEL_MASK;
7041e79841aSMartin Fuzzey 	reg |= i;
7051e79841aSMartin Fuzzey 
7061e79841aSMartin Fuzzey 	return mma8452_change_config(data, MMA8452_HP_FILTER_CUTOFF, reg);
7071e79841aSMartin Fuzzey }
7081e79841aSMartin Fuzzey 
709c7eeea93SPeter Meerwald static int mma8452_write_raw(struct iio_dev *indio_dev,
710c7eeea93SPeter Meerwald 			     struct iio_chan_spec const *chan,
711c7eeea93SPeter Meerwald 			     int val, int val2, long mask)
712c7eeea93SPeter Meerwald {
713c7eeea93SPeter Meerwald 	struct mma8452_data *data = iio_priv(indio_dev);
7141e79841aSMartin Fuzzey 	int i, ret;
715c7eeea93SPeter Meerwald 
71679de2ee4SJonathan Cameron 	ret = iio_device_claim_direct_mode(indio_dev);
71779de2ee4SJonathan Cameron 	if (ret)
71879de2ee4SJonathan Cameron 		return ret;
719c7eeea93SPeter Meerwald 
720c7eeea93SPeter Meerwald 	switch (mask) {
721c7eeea93SPeter Meerwald 	case IIO_CHAN_INFO_SAMP_FREQ:
722c7eeea93SPeter Meerwald 		i = mma8452_get_samp_freq_index(data, val, val2);
72379de2ee4SJonathan Cameron 		if (i < 0) {
72479de2ee4SJonathan Cameron 			ret = i;
72579de2ee4SJonathan Cameron 			break;
72679de2ee4SJonathan Cameron 		}
727c7eeea93SPeter Meerwald 		data->ctrl_reg1 &= ~MMA8452_CTRL_DR_MASK;
728c7eeea93SPeter Meerwald 		data->ctrl_reg1 |= i << MMA8452_CTRL_DR_SHIFT;
729686027fbSHartmut Knaack 
730a45d1238SRichard Tresidder 		data->sleep_val = mma8452_calculate_sleep(data);
731a45d1238SRichard Tresidder 
73279de2ee4SJonathan Cameron 		ret = mma8452_change_config(data, MMA8452_CTRL_REG1,
733c7eeea93SPeter Meerwald 					    data->ctrl_reg1);
73479de2ee4SJonathan Cameron 		break;
735c7eeea93SPeter Meerwald 	case IIO_CHAN_INFO_SCALE:
736c7eeea93SPeter Meerwald 		i = mma8452_get_scale_index(data, val, val2);
73779de2ee4SJonathan Cameron 		if (i < 0) {
73879de2ee4SJonathan Cameron 			ret = i;
73979de2ee4SJonathan Cameron 			break;
74079de2ee4SJonathan Cameron 		}
741686027fbSHartmut Knaack 
742c7eeea93SPeter Meerwald 		data->data_cfg &= ~MMA8452_DATA_CFG_FS_MASK;
743c7eeea93SPeter Meerwald 		data->data_cfg |= i;
744686027fbSHartmut Knaack 
74579de2ee4SJonathan Cameron 		ret = mma8452_change_config(data, MMA8452_DATA_CFG,
746c7eeea93SPeter Meerwald 					    data->data_cfg);
74779de2ee4SJonathan Cameron 		break;
748c7eeea93SPeter Meerwald 	case IIO_CHAN_INFO_CALIBBIAS:
74979de2ee4SJonathan Cameron 		if (val < -128 || val > 127) {
75079de2ee4SJonathan Cameron 			ret = -EINVAL;
75179de2ee4SJonathan Cameron 			break;
75279de2ee4SJonathan Cameron 		}
753686027fbSHartmut Knaack 
75479de2ee4SJonathan Cameron 		ret = mma8452_change_config(data,
755686027fbSHartmut Knaack 					    MMA8452_OFF_X + chan->scan_index,
756686027fbSHartmut Knaack 					    val);
75779de2ee4SJonathan Cameron 		break;
7581e79841aSMartin Fuzzey 
7591e79841aSMartin Fuzzey 	case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
7601e79841aSMartin Fuzzey 		if (val == 0 && val2 == 0) {
7611e79841aSMartin Fuzzey 			data->data_cfg &= ~MMA8452_DATA_CFG_HPF_MASK;
7621e79841aSMartin Fuzzey 		} else {
7631e79841aSMartin Fuzzey 			data->data_cfg |= MMA8452_DATA_CFG_HPF_MASK;
7641e79841aSMartin Fuzzey 			ret = mma8452_set_hp_filter_frequency(data, val, val2);
7651e79841aSMartin Fuzzey 			if (ret < 0)
76679de2ee4SJonathan Cameron 				break;
7671e79841aSMartin Fuzzey 		}
768686027fbSHartmut Knaack 
76979de2ee4SJonathan Cameron 		ret = mma8452_change_config(data, MMA8452_DATA_CFG,
7701e79841aSMartin Fuzzey 					     data->data_cfg);
77179de2ee4SJonathan Cameron 		break;
7721e79841aSMartin Fuzzey 
773ed859fc1SMartin Kepplinger 	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
774ed859fc1SMartin Kepplinger 		ret = mma8452_get_odr_index(data);
775ed859fc1SMartin Kepplinger 
776ed859fc1SMartin Kepplinger 		for (i = 0; i < ARRAY_SIZE(mma8452_os_ratio); i++) {
77779de2ee4SJonathan Cameron 			if (mma8452_os_ratio[i][ret] == val) {
77879de2ee4SJonathan Cameron 				ret = mma8452_set_power_mode(data, i);
77979de2ee4SJonathan Cameron 				break;
78079de2ee4SJonathan Cameron 			}
78179de2ee4SJonathan Cameron 		}
78279de2ee4SJonathan Cameron 		break;
78379de2ee4SJonathan Cameron 	default:
78479de2ee4SJonathan Cameron 		ret = -EINVAL;
78579de2ee4SJonathan Cameron 		break;
786ed859fc1SMartin Kepplinger 	}
787ed859fc1SMartin Kepplinger 
78879de2ee4SJonathan Cameron 	iio_device_release_direct_mode(indio_dev);
78979de2ee4SJonathan Cameron 	return ret;
790c7eeea93SPeter Meerwald }
791c7eeea93SPeter Meerwald 
792605f72deSHarinath Nampally static int mma8452_get_event_regs(struct mma8452_data *data,
793605f72deSHarinath Nampally 		const struct iio_chan_spec *chan, enum iio_event_direction dir,
794605f72deSHarinath Nampally 		const struct mma8452_event_regs **ev_reg)
795605f72deSHarinath Nampally {
796605f72deSHarinath Nampally 	if (!chan)
797605f72deSHarinath Nampally 		return -EINVAL;
798605f72deSHarinath Nampally 
799605f72deSHarinath Nampally 	switch (chan->type) {
800605f72deSHarinath Nampally 	case IIO_ACCEL:
801605f72deSHarinath Nampally 		switch (dir) {
802605f72deSHarinath Nampally 		case IIO_EV_DIR_RISING:
803605f72deSHarinath Nampally 			if ((data->chip_info->all_events
804605f72deSHarinath Nampally 					& MMA8452_INT_TRANS) &&
805605f72deSHarinath Nampally 				(data->chip_info->enabled_events
806605f72deSHarinath Nampally 					& MMA8452_INT_TRANS))
807a654c062SHarinath Nampally 				*ev_reg = &trans_ev_regs;
808605f72deSHarinath Nampally 			else
809a654c062SHarinath Nampally 				*ev_reg = &ff_mt_ev_regs;
810605f72deSHarinath Nampally 			return 0;
811605f72deSHarinath Nampally 		case IIO_EV_DIR_FALLING:
812a654c062SHarinath Nampally 			*ev_reg = &ff_mt_ev_regs;
813605f72deSHarinath Nampally 			return 0;
814605f72deSHarinath Nampally 		default:
815605f72deSHarinath Nampally 			return -EINVAL;
816605f72deSHarinath Nampally 		}
817605f72deSHarinath Nampally 	default:
818605f72deSHarinath Nampally 		return -EINVAL;
819605f72deSHarinath Nampally 	}
820605f72deSHarinath Nampally }
821605f72deSHarinath Nampally 
8224febd9f1SHarinath Nampally static int mma8452_read_event_value(struct iio_dev *indio_dev,
82328e34278SMartin Fuzzey 			       const struct iio_chan_spec *chan,
82428e34278SMartin Fuzzey 			       enum iio_event_type type,
82528e34278SMartin Fuzzey 			       enum iio_event_direction dir,
82628e34278SMartin Fuzzey 			       enum iio_event_info info,
82728e34278SMartin Fuzzey 			       int *val, int *val2)
82828e34278SMartin Fuzzey {
82928e34278SMartin Fuzzey 	struct mma8452_data *data = iio_priv(indio_dev);
830ed859fc1SMartin Kepplinger 	int ret, us, power_mode;
831605f72deSHarinath Nampally 	const struct mma8452_event_regs *ev_regs;
832605f72deSHarinath Nampally 
833605f72deSHarinath Nampally 	ret = mma8452_get_event_regs(data, chan, dir, &ev_regs);
834605f72deSHarinath Nampally 	if (ret)
835605f72deSHarinath Nampally 		return ret;
83628e34278SMartin Fuzzey 
8375dbbd19fSMartin Fuzzey 	switch (info) {
8385dbbd19fSMartin Fuzzey 	case IIO_EV_INFO_VALUE:
839605f72deSHarinath Nampally 		ret = i2c_smbus_read_byte_data(data->client, ev_regs->ev_ths);
84028e34278SMartin Fuzzey 		if (ret < 0)
84128e34278SMartin Fuzzey 			return ret;
84228e34278SMartin Fuzzey 
843605f72deSHarinath Nampally 		*val = ret & ev_regs->ev_ths_mask;
844686027fbSHartmut Knaack 
84528e34278SMartin Fuzzey 		return IIO_VAL_INT;
8465dbbd19fSMartin Fuzzey 
8475dbbd19fSMartin Fuzzey 	case IIO_EV_INFO_PERIOD:
848605f72deSHarinath Nampally 		ret = i2c_smbus_read_byte_data(data->client, ev_regs->ev_count);
8495dbbd19fSMartin Fuzzey 		if (ret < 0)
8505dbbd19fSMartin Fuzzey 			return ret;
8515dbbd19fSMartin Fuzzey 
852ed859fc1SMartin Kepplinger 		power_mode = mma8452_get_power_mode(data);
853ed859fc1SMartin Kepplinger 		if (power_mode < 0)
854ed859fc1SMartin Kepplinger 			return power_mode;
855ed859fc1SMartin Kepplinger 
856cc54a660SHarinath Nampally 		us = ret * mma8452_time_step_us[power_mode][
8575dbbd19fSMartin Fuzzey 				mma8452_get_odr_index(data)];
8585dbbd19fSMartin Fuzzey 		*val = us / USEC_PER_SEC;
8595dbbd19fSMartin Fuzzey 		*val2 = us % USEC_PER_SEC;
860686027fbSHartmut Knaack 
8615dbbd19fSMartin Fuzzey 		return IIO_VAL_INT_PLUS_MICRO;
8625dbbd19fSMartin Fuzzey 
8631e79841aSMartin Fuzzey 	case IIO_EV_INFO_HIGH_PASS_FILTER_3DB:
8641e79841aSMartin Fuzzey 		ret = i2c_smbus_read_byte_data(data->client,
8651e79841aSMartin Fuzzey 					       MMA8452_TRANSIENT_CFG);
8661e79841aSMartin Fuzzey 		if (ret < 0)
8671e79841aSMartin Fuzzey 			return ret;
8681e79841aSMartin Fuzzey 
8691e79841aSMartin Fuzzey 		if (ret & MMA8452_TRANSIENT_CFG_HPF_BYP) {
8701e79841aSMartin Fuzzey 			*val = 0;
8711e79841aSMartin Fuzzey 			*val2 = 0;
8721e79841aSMartin Fuzzey 		} else {
8731e79841aSMartin Fuzzey 			ret = mma8452_read_hp_filter(data, val, val2);
8741e79841aSMartin Fuzzey 			if (ret < 0)
8751e79841aSMartin Fuzzey 				return ret;
8761e79841aSMartin Fuzzey 		}
877686027fbSHartmut Knaack 
8781e79841aSMartin Fuzzey 		return IIO_VAL_INT_PLUS_MICRO;
8791e79841aSMartin Fuzzey 
8805dbbd19fSMartin Fuzzey 	default:
8815dbbd19fSMartin Fuzzey 		return -EINVAL;
8825dbbd19fSMartin Fuzzey 	}
88328e34278SMartin Fuzzey }
88428e34278SMartin Fuzzey 
8854febd9f1SHarinath Nampally static int mma8452_write_event_value(struct iio_dev *indio_dev,
88628e34278SMartin Fuzzey 				const struct iio_chan_spec *chan,
88728e34278SMartin Fuzzey 				enum iio_event_type type,
88828e34278SMartin Fuzzey 				enum iio_event_direction dir,
88928e34278SMartin Fuzzey 				enum iio_event_info info,
89028e34278SMartin Fuzzey 				int val, int val2)
89128e34278SMartin Fuzzey {
89228e34278SMartin Fuzzey 	struct mma8452_data *data = iio_priv(indio_dev);
8931e79841aSMartin Fuzzey 	int ret, reg, steps;
894605f72deSHarinath Nampally 	const struct mma8452_event_regs *ev_regs;
895605f72deSHarinath Nampally 
896605f72deSHarinath Nampally 	ret = mma8452_get_event_regs(data, chan, dir, &ev_regs);
897605f72deSHarinath Nampally 	if (ret)
898605f72deSHarinath Nampally 		return ret;
89928e34278SMartin Fuzzey 
9005dbbd19fSMartin Fuzzey 	switch (info) {
9015dbbd19fSMartin Fuzzey 	case IIO_EV_INFO_VALUE:
902605f72deSHarinath Nampally 		if (val < 0 || val > ev_regs->ev_ths_mask)
90311218226SHartmut Knaack 			return -EINVAL;
90411218226SHartmut Knaack 
905605f72deSHarinath Nampally 		return mma8452_change_config(data, ev_regs->ev_ths, val);
9065dbbd19fSMartin Fuzzey 
9075dbbd19fSMartin Fuzzey 	case IIO_EV_INFO_PERIOD:
908ed859fc1SMartin Kepplinger 		ret = mma8452_get_power_mode(data);
909ed859fc1SMartin Kepplinger 		if (ret < 0)
910ed859fc1SMartin Kepplinger 			return ret;
911ed859fc1SMartin Kepplinger 
9125dbbd19fSMartin Fuzzey 		steps = (val * USEC_PER_SEC + val2) /
913cc54a660SHarinath Nampally 				mma8452_time_step_us[ret][
9145dbbd19fSMartin Fuzzey 					mma8452_get_odr_index(data)];
9155dbbd19fSMartin Fuzzey 
91611218226SHartmut Knaack 		if (steps < 0 || steps > 0xff)
9175dbbd19fSMartin Fuzzey 			return -EINVAL;
9185dbbd19fSMartin Fuzzey 
919605f72deSHarinath Nampally 		return mma8452_change_config(data, ev_regs->ev_count, steps);
920686027fbSHartmut Knaack 
9211e79841aSMartin Fuzzey 	case IIO_EV_INFO_HIGH_PASS_FILTER_3DB:
9221e79841aSMartin Fuzzey 		reg = i2c_smbus_read_byte_data(data->client,
9231e79841aSMartin Fuzzey 					       MMA8452_TRANSIENT_CFG);
9241e79841aSMartin Fuzzey 		if (reg < 0)
9251e79841aSMartin Fuzzey 			return reg;
9261e79841aSMartin Fuzzey 
9271e79841aSMartin Fuzzey 		if (val == 0 && val2 == 0) {
9281e79841aSMartin Fuzzey 			reg |= MMA8452_TRANSIENT_CFG_HPF_BYP;
9291e79841aSMartin Fuzzey 		} else {
9301e79841aSMartin Fuzzey 			reg &= ~MMA8452_TRANSIENT_CFG_HPF_BYP;
9311e79841aSMartin Fuzzey 			ret = mma8452_set_hp_filter_frequency(data, val, val2);
9321e79841aSMartin Fuzzey 			if (ret < 0)
9331e79841aSMartin Fuzzey 				return ret;
9341e79841aSMartin Fuzzey 		}
935686027fbSHartmut Knaack 
9361e79841aSMartin Fuzzey 		return mma8452_change_config(data, MMA8452_TRANSIENT_CFG, reg);
9371e79841aSMartin Fuzzey 
9385dbbd19fSMartin Fuzzey 	default:
9395dbbd19fSMartin Fuzzey 		return -EINVAL;
9405dbbd19fSMartin Fuzzey 	}
94128e34278SMartin Fuzzey }
94228e34278SMartin Fuzzey 
94328e34278SMartin Fuzzey static int mma8452_read_event_config(struct iio_dev *indio_dev,
94428e34278SMartin Fuzzey 				     const struct iio_chan_spec *chan,
94528e34278SMartin Fuzzey 				     enum iio_event_type type,
94628e34278SMartin Fuzzey 				     enum iio_event_direction dir)
94728e34278SMartin Fuzzey {
94828e34278SMartin Fuzzey 	struct mma8452_data *data = iio_priv(indio_dev);
94928e34278SMartin Fuzzey 	int ret;
950605f72deSHarinath Nampally 	const struct mma8452_event_regs *ev_regs;
951605f72deSHarinath Nampally 
952605f72deSHarinath Nampally 	ret = mma8452_get_event_regs(data, chan, dir, &ev_regs);
953605f72deSHarinath Nampally 	if (ret)
954605f72deSHarinath Nampally 		return ret;
95528e34278SMartin Fuzzey 
9564b04266aSMartin Kepplinger 	switch (dir) {
9574b04266aSMartin Kepplinger 	case IIO_EV_DIR_FALLING:
9584b04266aSMartin Kepplinger 		return mma8452_freefall_mode_enabled(data);
9594b04266aSMartin Kepplinger 	case IIO_EV_DIR_RISING:
960c3cdd6e4SMartin Kepplinger 		ret = i2c_smbus_read_byte_data(data->client,
961605f72deSHarinath Nampally 				ev_regs->ev_cfg);
96228e34278SMartin Fuzzey 		if (ret < 0)
96328e34278SMartin Fuzzey 			return ret;
96428e34278SMartin Fuzzey 
9658b8ff3a6SMartin Kepplinger 		return !!(ret & BIT(chan->scan_index +
966605f72deSHarinath Nampally 				ev_regs->ev_cfg_chan_shift));
9674b04266aSMartin Kepplinger 	default:
9684b04266aSMartin Kepplinger 		return -EINVAL;
9694b04266aSMartin Kepplinger 	}
97028e34278SMartin Fuzzey }
97128e34278SMartin Fuzzey 
97228e34278SMartin Fuzzey static int mma8452_write_event_config(struct iio_dev *indio_dev,
97328e34278SMartin Fuzzey 				      const struct iio_chan_spec *chan,
97428e34278SMartin Fuzzey 				      enum iio_event_type type,
97528e34278SMartin Fuzzey 				      enum iio_event_direction dir,
97628e34278SMartin Fuzzey 				      int state)
97728e34278SMartin Fuzzey {
97828e34278SMartin Fuzzey 	struct mma8452_data *data = iio_priv(indio_dev);
97996c0cb2bSMartin Kepplinger 	int val, ret;
980605f72deSHarinath Nampally 	const struct mma8452_event_regs *ev_regs;
981605f72deSHarinath Nampally 
982605f72deSHarinath Nampally 	ret = mma8452_get_event_regs(data, chan, dir, &ev_regs);
983605f72deSHarinath Nampally 	if (ret)
984605f72deSHarinath Nampally 		return ret;
98596c0cb2bSMartin Kepplinger 
98696c0cb2bSMartin Kepplinger 	ret = mma8452_set_runtime_pm_state(data->client, state);
98796c0cb2bSMartin Kepplinger 	if (ret)
98896c0cb2bSMartin Kepplinger 		return ret;
98928e34278SMartin Fuzzey 
9904b04266aSMartin Kepplinger 	switch (dir) {
9914b04266aSMartin Kepplinger 	case IIO_EV_DIR_FALLING:
9924b04266aSMartin Kepplinger 		return mma8452_set_freefall_mode(data, state);
9934b04266aSMartin Kepplinger 	case IIO_EV_DIR_RISING:
994605f72deSHarinath Nampally 		val = i2c_smbus_read_byte_data(data->client, ev_regs->ev_cfg);
99528e34278SMartin Fuzzey 		if (val < 0)
99628e34278SMartin Fuzzey 			return val;
99728e34278SMartin Fuzzey 
9984b04266aSMartin Kepplinger 		if (state) {
9994b04266aSMartin Kepplinger 			if (mma8452_freefall_mode_enabled(data)) {
1000605f72deSHarinath Nampally 				val &= ~BIT(idx_x + ev_regs->ev_cfg_chan_shift);
1001605f72deSHarinath Nampally 				val &= ~BIT(idx_y + ev_regs->ev_cfg_chan_shift);
1002605f72deSHarinath Nampally 				val &= ~BIT(idx_z + ev_regs->ev_cfg_chan_shift);
10034b04266aSMartin Kepplinger 				val |= MMA8452_FF_MT_CFG_OAE;
10044b04266aSMartin Kepplinger 			}
1005605f72deSHarinath Nampally 			val |= BIT(chan->scan_index +
1006605f72deSHarinath Nampally 					ev_regs->ev_cfg_chan_shift);
10074b04266aSMartin Kepplinger 		} else {
10084b04266aSMartin Kepplinger 			if (mma8452_freefall_mode_enabled(data))
10094b04266aSMartin Kepplinger 				return 0;
10104b04266aSMartin Kepplinger 
1011605f72deSHarinath Nampally 			val &= ~BIT(chan->scan_index +
1012605f72deSHarinath Nampally 					ev_regs->ev_cfg_chan_shift);
10134b04266aSMartin Kepplinger 		}
101428e34278SMartin Fuzzey 
1015605f72deSHarinath Nampally 		val |= ev_regs->ev_cfg_ele;
101628e34278SMartin Fuzzey 
1017605f72deSHarinath Nampally 		return mma8452_change_config(data, ev_regs->ev_cfg, val);
10184b04266aSMartin Kepplinger 	default:
10194b04266aSMartin Kepplinger 		return -EINVAL;
10204b04266aSMartin Kepplinger 	}
102128e34278SMartin Fuzzey }
102228e34278SMartin Fuzzey 
102328e34278SMartin Fuzzey static void mma8452_transient_interrupt(struct iio_dev *indio_dev)
102428e34278SMartin Fuzzey {
102528e34278SMartin Fuzzey 	struct mma8452_data *data = iio_priv(indio_dev);
1026bc2b7dabSGregor Boirie 	s64 ts = iio_get_time_ns(indio_dev);
102728e34278SMartin Fuzzey 	int src;
102828e34278SMartin Fuzzey 
1029605f72deSHarinath Nampally 	src = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_SRC);
103028e34278SMartin Fuzzey 	if (src < 0)
103128e34278SMartin Fuzzey 		return;
103228e34278SMartin Fuzzey 
1033605f72deSHarinath Nampally 	if (src & MMA8452_TRANSIENT_SRC_XTRANSE)
103428e34278SMartin Fuzzey 		iio_push_event(indio_dev,
103528e34278SMartin Fuzzey 			       IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X,
1036c5d0db06SMartin Kepplinger 						  IIO_EV_TYPE_MAG,
103728e34278SMartin Fuzzey 						  IIO_EV_DIR_RISING),
103828e34278SMartin Fuzzey 			       ts);
103928e34278SMartin Fuzzey 
1040605f72deSHarinath Nampally 	if (src & MMA8452_TRANSIENT_SRC_YTRANSE)
104128e34278SMartin Fuzzey 		iio_push_event(indio_dev,
104228e34278SMartin Fuzzey 			       IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Y,
1043c5d0db06SMartin Kepplinger 						  IIO_EV_TYPE_MAG,
104428e34278SMartin Fuzzey 						  IIO_EV_DIR_RISING),
104528e34278SMartin Fuzzey 			       ts);
104628e34278SMartin Fuzzey 
1047605f72deSHarinath Nampally 	if (src & MMA8452_TRANSIENT_SRC_ZTRANSE)
104828e34278SMartin Fuzzey 		iio_push_event(indio_dev,
104928e34278SMartin Fuzzey 			       IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Z,
1050c5d0db06SMartin Kepplinger 						  IIO_EV_TYPE_MAG,
105128e34278SMartin Fuzzey 						  IIO_EV_DIR_RISING),
105228e34278SMartin Fuzzey 			       ts);
105328e34278SMartin Fuzzey }
105428e34278SMartin Fuzzey 
105528e34278SMartin Fuzzey static irqreturn_t mma8452_interrupt(int irq, void *p)
105628e34278SMartin Fuzzey {
105728e34278SMartin Fuzzey 	struct iio_dev *indio_dev = p;
105828e34278SMartin Fuzzey 	struct mma8452_data *data = iio_priv(indio_dev);
1059eb046989SLars-Peter Clausen 	irqreturn_t ret = IRQ_NONE;
106028e34278SMartin Fuzzey 	int src;
106128e34278SMartin Fuzzey 
106228e34278SMartin Fuzzey 	src = i2c_smbus_read_byte_data(data->client, MMA8452_INT_SRC);
106328e34278SMartin Fuzzey 	if (src < 0)
106428e34278SMartin Fuzzey 		return IRQ_NONE;
106528e34278SMartin Fuzzey 
1066b02ec67aSLeonard Crestez 	if (!(src & (data->chip_info->enabled_events | MMA8452_INT_DRDY)))
1067605f72deSHarinath Nampally 		return IRQ_NONE;
1068605f72deSHarinath Nampally 
1069ae6d9ce0SMartin Fuzzey 	if (src & MMA8452_INT_DRDY) {
1070ae6d9ce0SMartin Fuzzey 		iio_trigger_poll_chained(indio_dev->trig);
1071ae6d9ce0SMartin Fuzzey 		ret = IRQ_HANDLED;
107228e34278SMartin Fuzzey 	}
107328e34278SMartin Fuzzey 
1074605f72deSHarinath Nampally 	if (src & MMA8452_INT_FF_MT) {
1075605f72deSHarinath Nampally 		if (mma8452_freefall_mode_enabled(data)) {
1076605f72deSHarinath Nampally 			s64 ts = iio_get_time_ns(indio_dev);
1077605f72deSHarinath Nampally 
1078605f72deSHarinath Nampally 			iio_push_event(indio_dev,
1079605f72deSHarinath Nampally 				       IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
1080605f72deSHarinath Nampally 							  IIO_MOD_X_AND_Y_AND_Z,
1081605f72deSHarinath Nampally 							  IIO_EV_TYPE_MAG,
1082605f72deSHarinath Nampally 							  IIO_EV_DIR_FALLING),
1083605f72deSHarinath Nampally 					ts);
1084605f72deSHarinath Nampally 		}
1085605f72deSHarinath Nampally 		ret = IRQ_HANDLED;
1086605f72deSHarinath Nampally 	}
1087605f72deSHarinath Nampally 
1088605f72deSHarinath Nampally 	if (src & MMA8452_INT_TRANS) {
1089ae6d9ce0SMartin Fuzzey 		mma8452_transient_interrupt(indio_dev);
1090ae6d9ce0SMartin Fuzzey 		ret = IRQ_HANDLED;
1091ae6d9ce0SMartin Fuzzey 	}
1092ae6d9ce0SMartin Fuzzey 
1093ae6d9ce0SMartin Fuzzey 	return ret;
109428e34278SMartin Fuzzey }
109528e34278SMartin Fuzzey 
1096c7eeea93SPeter Meerwald static irqreturn_t mma8452_trigger_handler(int irq, void *p)
1097c7eeea93SPeter Meerwald {
1098c7eeea93SPeter Meerwald 	struct iio_poll_func *pf = p;
1099c7eeea93SPeter Meerwald 	struct iio_dev *indio_dev = pf->indio_dev;
1100c7eeea93SPeter Meerwald 	struct mma8452_data *data = iio_priv(indio_dev);
1101c7eeea93SPeter Meerwald 	int ret;
1102c7eeea93SPeter Meerwald 
110389226a29SJonathan Cameron 	ret = mma8452_read(data, data->buffer.channels);
1104c7eeea93SPeter Meerwald 	if (ret < 0)
1105c7eeea93SPeter Meerwald 		goto done;
1106c7eeea93SPeter Meerwald 
110789226a29SJonathan Cameron 	iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer,
1108bc2b7dabSGregor Boirie 					   iio_get_time_ns(indio_dev));
1109c7eeea93SPeter Meerwald 
1110c7eeea93SPeter Meerwald done:
1111c7eeea93SPeter Meerwald 	iio_trigger_notify_done(indio_dev->trig);
1112686027fbSHartmut Knaack 
1113c7eeea93SPeter Meerwald 	return IRQ_HANDLED;
1114c7eeea93SPeter Meerwald }
1115c7eeea93SPeter Meerwald 
11162a17698cSMartin Fuzzey static int mma8452_reg_access_dbg(struct iio_dev *indio_dev,
1117f8b7b30fSHarinath Nampally 				  unsigned int reg, unsigned int writeval,
1118f8b7b30fSHarinath Nampally 				  unsigned int *readval)
11192a17698cSMartin Fuzzey {
11202a17698cSMartin Fuzzey 	int ret;
11212a17698cSMartin Fuzzey 	struct mma8452_data *data = iio_priv(indio_dev);
11222a17698cSMartin Fuzzey 
11232a17698cSMartin Fuzzey 	if (reg > MMA8452_MAX_REG)
11242a17698cSMartin Fuzzey 		return -EINVAL;
11252a17698cSMartin Fuzzey 
11262a17698cSMartin Fuzzey 	if (!readval)
11272a17698cSMartin Fuzzey 		return mma8452_change_config(data, reg, writeval);
11282a17698cSMartin Fuzzey 
11292a17698cSMartin Fuzzey 	ret = i2c_smbus_read_byte_data(data->client, reg);
11302a17698cSMartin Fuzzey 	if (ret < 0)
11312a17698cSMartin Fuzzey 		return ret;
11322a17698cSMartin Fuzzey 
11332a17698cSMartin Fuzzey 	*readval = ret;
11342a17698cSMartin Fuzzey 
11352a17698cSMartin Fuzzey 	return 0;
11362a17698cSMartin Fuzzey }
11372a17698cSMartin Fuzzey 
11384b04266aSMartin Kepplinger static const struct iio_event_spec mma8452_freefall_event[] = {
11394b04266aSMartin Kepplinger 	{
11404b04266aSMartin Kepplinger 		.type = IIO_EV_TYPE_MAG,
11414b04266aSMartin Kepplinger 		.dir = IIO_EV_DIR_FALLING,
11424b04266aSMartin Kepplinger 		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
11434b04266aSMartin Kepplinger 		.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
11444b04266aSMartin Kepplinger 					BIT(IIO_EV_INFO_PERIOD) |
11454b04266aSMartin Kepplinger 					BIT(IIO_EV_INFO_HIGH_PASS_FILTER_3DB)
11464b04266aSMartin Kepplinger 	},
11474b04266aSMartin Kepplinger };
11484b04266aSMartin Kepplinger 
11494b04266aSMartin Kepplinger static const struct iio_event_spec mma8652_freefall_event[] = {
11504b04266aSMartin Kepplinger 	{
11514b04266aSMartin Kepplinger 		.type = IIO_EV_TYPE_MAG,
11524b04266aSMartin Kepplinger 		.dir = IIO_EV_DIR_FALLING,
11534b04266aSMartin Kepplinger 		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
11544b04266aSMartin Kepplinger 		.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
11554b04266aSMartin Kepplinger 					BIT(IIO_EV_INFO_PERIOD)
11564b04266aSMartin Kepplinger 	},
11574b04266aSMartin Kepplinger };
11584b04266aSMartin Kepplinger 
115928e34278SMartin Fuzzey static const struct iio_event_spec mma8452_transient_event[] = {
116028e34278SMartin Fuzzey 	{
1161c5d0db06SMartin Kepplinger 		.type = IIO_EV_TYPE_MAG,
116228e34278SMartin Fuzzey 		.dir = IIO_EV_DIR_RISING,
116328e34278SMartin Fuzzey 		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
11645dbbd19fSMartin Fuzzey 		.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
11651e79841aSMartin Fuzzey 					BIT(IIO_EV_INFO_PERIOD) |
11661e79841aSMartin Fuzzey 					BIT(IIO_EV_INFO_HIGH_PASS_FILTER_3DB)
116728e34278SMartin Fuzzey 	},
116828e34278SMartin Fuzzey };
116928e34278SMartin Fuzzey 
117060f562e7SMartin Kepplinger static const struct iio_event_spec mma8452_motion_event[] = {
117160f562e7SMartin Kepplinger 	{
117260f562e7SMartin Kepplinger 		.type = IIO_EV_TYPE_MAG,
117360f562e7SMartin Kepplinger 		.dir = IIO_EV_DIR_RISING,
117460f562e7SMartin Kepplinger 		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
117560f562e7SMartin Kepplinger 		.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
117660f562e7SMartin Kepplinger 					BIT(IIO_EV_INFO_PERIOD)
117760f562e7SMartin Kepplinger 	},
117860f562e7SMartin Kepplinger };
117960f562e7SMartin Kepplinger 
118028e34278SMartin Fuzzey /*
118128e34278SMartin Fuzzey  * Threshold is configured in fixed 8G/127 steps regardless of
118228e34278SMartin Fuzzey  * currently selected scale for measurement.
118328e34278SMartin Fuzzey  */
118428e34278SMartin Fuzzey static IIO_CONST_ATTR_NAMED(accel_transient_scale, in_accel_scale, "0.617742");
118528e34278SMartin Fuzzey 
118628e34278SMartin Fuzzey static struct attribute *mma8452_event_attributes[] = {
118728e34278SMartin Fuzzey 	&iio_const_attr_accel_transient_scale.dev_attr.attr,
118828e34278SMartin Fuzzey 	NULL,
118928e34278SMartin Fuzzey };
119028e34278SMartin Fuzzey 
1191681ab2ceSRikard Falkeborn static const struct attribute_group mma8452_event_attribute_group = {
119228e34278SMartin Fuzzey 	.attrs = mma8452_event_attributes,
119328e34278SMartin Fuzzey };
119428e34278SMartin Fuzzey 
1195b863f2e3SHans de Goede static const struct iio_mount_matrix *
1196b863f2e3SHans de Goede mma8452_get_mount_matrix(const struct iio_dev *indio_dev,
1197b863f2e3SHans de Goede 			   const struct iio_chan_spec *chan)
1198b863f2e3SHans de Goede {
1199b863f2e3SHans de Goede 	struct mma8452_data *data = iio_priv(indio_dev);
1200b863f2e3SHans de Goede 
1201b863f2e3SHans de Goede 	return &data->orientation;
1202b863f2e3SHans de Goede }
1203b863f2e3SHans de Goede 
1204b863f2e3SHans de Goede static const struct iio_chan_spec_ext_info mma8452_ext_info[] = {
1205b863f2e3SHans de Goede 	IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, mma8452_get_mount_matrix),
1206b863f2e3SHans de Goede 	{ }
1207b863f2e3SHans de Goede };
1208b863f2e3SHans de Goede 
12094b04266aSMartin Kepplinger #define MMA8452_FREEFALL_CHANNEL(modifier) { \
12104b04266aSMartin Kepplinger 	.type = IIO_ACCEL, \
12114b04266aSMartin Kepplinger 	.modified = 1, \
12124b04266aSMartin Kepplinger 	.channel2 = modifier, \
12134b04266aSMartin Kepplinger 	.scan_index = -1, \
12144b04266aSMartin Kepplinger 	.event_spec = mma8452_freefall_event, \
12154b04266aSMartin Kepplinger 	.num_event_specs = ARRAY_SIZE(mma8452_freefall_event), \
12164b04266aSMartin Kepplinger }
12174b04266aSMartin Kepplinger 
12184b04266aSMartin Kepplinger #define MMA8652_FREEFALL_CHANNEL(modifier) { \
12194b04266aSMartin Kepplinger 	.type = IIO_ACCEL, \
12204b04266aSMartin Kepplinger 	.modified = 1, \
12214b04266aSMartin Kepplinger 	.channel2 = modifier, \
12224b04266aSMartin Kepplinger 	.scan_index = -1, \
12234b04266aSMartin Kepplinger 	.event_spec = mma8652_freefall_event, \
12244b04266aSMartin Kepplinger 	.num_event_specs = ARRAY_SIZE(mma8652_freefall_event), \
12254b04266aSMartin Kepplinger }
12264b04266aSMartin Kepplinger 
1227c3cdd6e4SMartin Kepplinger #define MMA8452_CHANNEL(axis, idx, bits) { \
1228c7eeea93SPeter Meerwald 	.type = IIO_ACCEL, \
1229c7eeea93SPeter Meerwald 	.modified = 1, \
1230c7eeea93SPeter Meerwald 	.channel2 = IIO_MOD_##axis, \
1231c7eeea93SPeter Meerwald 	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
1232c7eeea93SPeter Meerwald 			      BIT(IIO_CHAN_INFO_CALIBBIAS), \
1233c7eeea93SPeter Meerwald 	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
12341e79841aSMartin Fuzzey 			BIT(IIO_CHAN_INFO_SCALE) | \
1235ed859fc1SMartin Kepplinger 			BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY) | \
1236ed859fc1SMartin Kepplinger 			BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
1237c7eeea93SPeter Meerwald 	.scan_index = idx, \
1238c7eeea93SPeter Meerwald 	.scan_type = { \
1239c7eeea93SPeter Meerwald 		.sign = 's', \
1240c3cdd6e4SMartin Kepplinger 		.realbits = (bits), \
1241c7eeea93SPeter Meerwald 		.storagebits = 16, \
1242c3cdd6e4SMartin Kepplinger 		.shift = 16 - (bits), \
1243c7eeea93SPeter Meerwald 		.endianness = IIO_BE, \
1244c7eeea93SPeter Meerwald 	}, \
124528e34278SMartin Fuzzey 	.event_spec = mma8452_transient_event, \
124628e34278SMartin Fuzzey 	.num_event_specs = ARRAY_SIZE(mma8452_transient_event), \
1247b863f2e3SHans de Goede 	.ext_info = mma8452_ext_info, \
1248c7eeea93SPeter Meerwald }
1249c7eeea93SPeter Meerwald 
1250417e008bSMartin Kepplinger #define MMA8652_CHANNEL(axis, idx, bits) { \
1251417e008bSMartin Kepplinger 	.type = IIO_ACCEL, \
1252417e008bSMartin Kepplinger 	.modified = 1, \
1253417e008bSMartin Kepplinger 	.channel2 = IIO_MOD_##axis, \
1254417e008bSMartin Kepplinger 	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
1255417e008bSMartin Kepplinger 		BIT(IIO_CHAN_INFO_CALIBBIAS), \
1256417e008bSMartin Kepplinger 	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
1257ed859fc1SMartin Kepplinger 		BIT(IIO_CHAN_INFO_SCALE) | \
1258ed859fc1SMartin Kepplinger 		BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
1259417e008bSMartin Kepplinger 	.scan_index = idx, \
1260417e008bSMartin Kepplinger 	.scan_type = { \
1261417e008bSMartin Kepplinger 		.sign = 's', \
1262417e008bSMartin Kepplinger 		.realbits = (bits), \
1263417e008bSMartin Kepplinger 		.storagebits = 16, \
1264417e008bSMartin Kepplinger 		.shift = 16 - (bits), \
1265417e008bSMartin Kepplinger 		.endianness = IIO_BE, \
1266417e008bSMartin Kepplinger 	}, \
1267417e008bSMartin Kepplinger 	.event_spec = mma8452_motion_event, \
1268417e008bSMartin Kepplinger 	.num_event_specs = ARRAY_SIZE(mma8452_motion_event), \
1269b863f2e3SHans de Goede 	.ext_info = mma8452_ext_info, \
1270417e008bSMartin Kepplinger }
1271417e008bSMartin Kepplinger 
1272244a93f6SMartin Kepplinger static const struct iio_chan_spec mma8451_channels[] = {
1273244a93f6SMartin Kepplinger 	MMA8452_CHANNEL(X, idx_x, 14),
1274244a93f6SMartin Kepplinger 	MMA8452_CHANNEL(Y, idx_y, 14),
1275244a93f6SMartin Kepplinger 	MMA8452_CHANNEL(Z, idx_z, 14),
1276244a93f6SMartin Kepplinger 	IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
1277244a93f6SMartin Kepplinger 	MMA8452_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
1278244a93f6SMartin Kepplinger };
1279244a93f6SMartin Kepplinger 
1280c7eeea93SPeter Meerwald static const struct iio_chan_spec mma8452_channels[] = {
1281e60378c1SMartin Kepplinger 	MMA8452_CHANNEL(X, idx_x, 12),
1282e60378c1SMartin Kepplinger 	MMA8452_CHANNEL(Y, idx_y, 12),
1283e60378c1SMartin Kepplinger 	MMA8452_CHANNEL(Z, idx_z, 12),
1284e60378c1SMartin Kepplinger 	IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
12854b04266aSMartin Kepplinger 	MMA8452_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
1286c7eeea93SPeter Meerwald };
1287c7eeea93SPeter Meerwald 
1288c5ea1b58SMartin Kepplinger static const struct iio_chan_spec mma8453_channels[] = {
1289e60378c1SMartin Kepplinger 	MMA8452_CHANNEL(X, idx_x, 10),
1290e60378c1SMartin Kepplinger 	MMA8452_CHANNEL(Y, idx_y, 10),
1291e60378c1SMartin Kepplinger 	MMA8452_CHANNEL(Z, idx_z, 10),
1292e60378c1SMartin Kepplinger 	IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
12934b04266aSMartin Kepplinger 	MMA8452_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
1294c5ea1b58SMartin Kepplinger };
1295c5ea1b58SMartin Kepplinger 
1296417e008bSMartin Kepplinger static const struct iio_chan_spec mma8652_channels[] = {
1297e60378c1SMartin Kepplinger 	MMA8652_CHANNEL(X, idx_x, 12),
1298e60378c1SMartin Kepplinger 	MMA8652_CHANNEL(Y, idx_y, 12),
1299e60378c1SMartin Kepplinger 	MMA8652_CHANNEL(Z, idx_z, 12),
1300e60378c1SMartin Kepplinger 	IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
13014b04266aSMartin Kepplinger 	MMA8652_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
1302417e008bSMartin Kepplinger };
1303417e008bSMartin Kepplinger 
1304417e008bSMartin Kepplinger static const struct iio_chan_spec mma8653_channels[] = {
1305e60378c1SMartin Kepplinger 	MMA8652_CHANNEL(X, idx_x, 10),
1306e60378c1SMartin Kepplinger 	MMA8652_CHANNEL(Y, idx_y, 10),
1307e60378c1SMartin Kepplinger 	MMA8652_CHANNEL(Z, idx_z, 10),
1308e60378c1SMartin Kepplinger 	IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
13094b04266aSMartin Kepplinger 	MMA8652_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
1310417e008bSMartin Kepplinger };
1311417e008bSMartin Kepplinger 
1312c3cdd6e4SMartin Kepplinger enum {
1313244a93f6SMartin Kepplinger 	mma8451,
1314c3cdd6e4SMartin Kepplinger 	mma8452,
1315c5ea1b58SMartin Kepplinger 	mma8453,
1316417e008bSMartin Kepplinger 	mma8652,
1317417e008bSMartin Kepplinger 	mma8653,
1318e8731180SMartin Kepplinger 	fxls8471,
1319c3cdd6e4SMartin Kepplinger };
1320c3cdd6e4SMartin Kepplinger 
1321c3cdd6e4SMartin Kepplinger static const struct mma_chip_info mma_chip_info_table[] = {
1322244a93f6SMartin Kepplinger 	[mma8451] = {
1323a47ac019SHans de Goede 		.name = "mma8451",
1324244a93f6SMartin Kepplinger 		.chip_id = MMA8451_DEVICE_ID,
1325244a93f6SMartin Kepplinger 		.channels = mma8451_channels,
1326244a93f6SMartin Kepplinger 		.num_channels = ARRAY_SIZE(mma8451_channels),
1327c3cdd6e4SMartin Kepplinger 		/*
1328c3cdd6e4SMartin Kepplinger 		 * Hardware has fullscale of -2G, -4G, -8G corresponding to
1329244a93f6SMartin Kepplinger 		 * raw value -8192 for 14 bit, -2048 for 12 bit or -512 for 10
1330244a93f6SMartin Kepplinger 		 * bit.
1331c3cdd6e4SMartin Kepplinger 		 * The userspace interface uses m/s^2 and we declare micro units
1332c3cdd6e4SMartin Kepplinger 		 * So scale factor for 12 bit here is given by:
1333c3cdd6e4SMartin Kepplinger 		 *	g * N * 1000000 / 2048 for N = 2, 4, 8 and g=9.80665
1334c3cdd6e4SMartin Kepplinger 		 */
1335244a93f6SMartin Kepplinger 		.mma_scales = { {0, 2394}, {0, 4788}, {0, 9577} },
1336605f72deSHarinath Nampally 		/*
1337605f72deSHarinath Nampally 		 * Although we enable the interrupt sources once and for
1338605f72deSHarinath Nampally 		 * all here the event detection itself is not enabled until
1339605f72deSHarinath Nampally 		 * userspace asks for it by mma8452_write_event_config()
1340605f72deSHarinath Nampally 		 */
1341605f72deSHarinath Nampally 		.all_events = MMA8452_INT_DRDY |
1342605f72deSHarinath Nampally 					MMA8452_INT_TRANS |
1343605f72deSHarinath Nampally 					MMA8452_INT_FF_MT,
1344605f72deSHarinath Nampally 		.enabled_events = MMA8452_INT_TRANS |
1345605f72deSHarinath Nampally 					MMA8452_INT_FF_MT,
1346244a93f6SMartin Kepplinger 	},
1347244a93f6SMartin Kepplinger 	[mma8452] = {
1348a47ac019SHans de Goede 		.name = "mma8452",
1349244a93f6SMartin Kepplinger 		.chip_id = MMA8452_DEVICE_ID,
1350244a93f6SMartin Kepplinger 		.channels = mma8452_channels,
1351244a93f6SMartin Kepplinger 		.num_channels = ARRAY_SIZE(mma8452_channels),
1352c3cdd6e4SMartin Kepplinger 		.mma_scales = { {0, 9577}, {0, 19154}, {0, 38307} },
1353605f72deSHarinath Nampally 		/*
1354605f72deSHarinath Nampally 		 * Although we enable the interrupt sources once and for
1355605f72deSHarinath Nampally 		 * all here the event detection itself is not enabled until
1356605f72deSHarinath Nampally 		 * userspace asks for it by mma8452_write_event_config()
1357605f72deSHarinath Nampally 		 */
1358605f72deSHarinath Nampally 		.all_events = MMA8452_INT_DRDY |
1359605f72deSHarinath Nampally 					MMA8452_INT_TRANS |
1360605f72deSHarinath Nampally 					MMA8452_INT_FF_MT,
1361605f72deSHarinath Nampally 		.enabled_events = MMA8452_INT_TRANS |
1362605f72deSHarinath Nampally 					MMA8452_INT_FF_MT,
1363c3cdd6e4SMartin Kepplinger 	},
1364c5ea1b58SMartin Kepplinger 	[mma8453] = {
1365a47ac019SHans de Goede 		.name = "mma8453",
1366c5ea1b58SMartin Kepplinger 		.chip_id = MMA8453_DEVICE_ID,
1367c5ea1b58SMartin Kepplinger 		.channels = mma8453_channels,
1368c5ea1b58SMartin Kepplinger 		.num_channels = ARRAY_SIZE(mma8453_channels),
1369c5ea1b58SMartin Kepplinger 		.mma_scales = { {0, 38307}, {0, 76614}, {0, 153228} },
1370605f72deSHarinath Nampally 		/*
1371605f72deSHarinath Nampally 		 * Although we enable the interrupt sources once and for
1372605f72deSHarinath Nampally 		 * all here the event detection itself is not enabled until
1373605f72deSHarinath Nampally 		 * userspace asks for it by mma8452_write_event_config()
1374605f72deSHarinath Nampally 		 */
1375605f72deSHarinath Nampally 		.all_events = MMA8452_INT_DRDY |
1376605f72deSHarinath Nampally 					MMA8452_INT_TRANS |
1377605f72deSHarinath Nampally 					MMA8452_INT_FF_MT,
1378605f72deSHarinath Nampally 		.enabled_events = MMA8452_INT_TRANS |
1379605f72deSHarinath Nampally 					MMA8452_INT_FF_MT,
1380c5ea1b58SMartin Kepplinger 	},
1381417e008bSMartin Kepplinger 	[mma8652] = {
1382a47ac019SHans de Goede 		.name = "mma8652",
1383417e008bSMartin Kepplinger 		.chip_id = MMA8652_DEVICE_ID,
1384417e008bSMartin Kepplinger 		.channels = mma8652_channels,
1385417e008bSMartin Kepplinger 		.num_channels = ARRAY_SIZE(mma8652_channels),
1386417e008bSMartin Kepplinger 		.mma_scales = { {0, 9577}, {0, 19154}, {0, 38307} },
1387605f72deSHarinath Nampally 		.all_events = MMA8452_INT_DRDY |
1388605f72deSHarinath Nampally 					MMA8452_INT_FF_MT,
1389605f72deSHarinath Nampally 		.enabled_events = MMA8452_INT_FF_MT,
1390417e008bSMartin Kepplinger 	},
1391417e008bSMartin Kepplinger 	[mma8653] = {
1392a47ac019SHans de Goede 		.name = "mma8653",
1393417e008bSMartin Kepplinger 		.chip_id = MMA8653_DEVICE_ID,
1394417e008bSMartin Kepplinger 		.channels = mma8653_channels,
1395417e008bSMartin Kepplinger 		.num_channels = ARRAY_SIZE(mma8653_channels),
1396417e008bSMartin Kepplinger 		.mma_scales = { {0, 38307}, {0, 76614}, {0, 153228} },
1397605f72deSHarinath Nampally 		/*
1398605f72deSHarinath Nampally 		 * Although we enable the interrupt sources once and for
1399605f72deSHarinath Nampally 		 * all here the event detection itself is not enabled until
1400605f72deSHarinath Nampally 		 * userspace asks for it by mma8452_write_event_config()
1401605f72deSHarinath Nampally 		 */
1402605f72deSHarinath Nampally 		.all_events = MMA8452_INT_DRDY |
1403605f72deSHarinath Nampally 					MMA8452_INT_FF_MT,
1404605f72deSHarinath Nampally 		.enabled_events = MMA8452_INT_FF_MT,
1405417e008bSMartin Kepplinger 	},
1406e8731180SMartin Kepplinger 	[fxls8471] = {
1407a47ac019SHans de Goede 		.name = "fxls8471",
1408e8731180SMartin Kepplinger 		.chip_id = FXLS8471_DEVICE_ID,
1409e8731180SMartin Kepplinger 		.channels = mma8451_channels,
1410e8731180SMartin Kepplinger 		.num_channels = ARRAY_SIZE(mma8451_channels),
1411e8731180SMartin Kepplinger 		.mma_scales = { {0, 2394}, {0, 4788}, {0, 9577} },
1412605f72deSHarinath Nampally 		/*
1413605f72deSHarinath Nampally 		 * Although we enable the interrupt sources once and for
1414605f72deSHarinath Nampally 		 * all here the event detection itself is not enabled until
1415605f72deSHarinath Nampally 		 * userspace asks for it by mma8452_write_event_config()
1416605f72deSHarinath Nampally 		 */
1417605f72deSHarinath Nampally 		.all_events = MMA8452_INT_DRDY |
1418605f72deSHarinath Nampally 					MMA8452_INT_TRANS |
1419605f72deSHarinath Nampally 					MMA8452_INT_FF_MT,
1420605f72deSHarinath Nampally 		.enabled_events = MMA8452_INT_TRANS |
1421605f72deSHarinath Nampally 					MMA8452_INT_FF_MT,
1422e8731180SMartin Kepplinger 	},
1423c3cdd6e4SMartin Kepplinger };
1424c3cdd6e4SMartin Kepplinger 
1425c7eeea93SPeter Meerwald static struct attribute *mma8452_attributes[] = {
1426c7eeea93SPeter Meerwald 	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
1427c7eeea93SPeter Meerwald 	&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
14281e79841aSMartin Fuzzey 	&iio_dev_attr_in_accel_filter_high_pass_3db_frequency_available.dev_attr.attr,
1429ed859fc1SMartin Kepplinger 	&iio_dev_attr_in_accel_oversampling_ratio_available.dev_attr.attr,
1430c7eeea93SPeter Meerwald 	NULL
1431c7eeea93SPeter Meerwald };
1432c7eeea93SPeter Meerwald 
1433c7eeea93SPeter Meerwald static const struct attribute_group mma8452_group = {
1434c7eeea93SPeter Meerwald 	.attrs = mma8452_attributes,
1435c7eeea93SPeter Meerwald };
1436c7eeea93SPeter Meerwald 
1437c7eeea93SPeter Meerwald static const struct iio_info mma8452_info = {
1438c7eeea93SPeter Meerwald 	.attrs = &mma8452_group,
1439c7eeea93SPeter Meerwald 	.read_raw = &mma8452_read_raw,
1440c7eeea93SPeter Meerwald 	.write_raw = &mma8452_write_raw,
144128e34278SMartin Fuzzey 	.event_attrs = &mma8452_event_attribute_group,
14424febd9f1SHarinath Nampally 	.read_event_value = &mma8452_read_event_value,
14434febd9f1SHarinath Nampally 	.write_event_value = &mma8452_write_event_value,
144428e34278SMartin Fuzzey 	.read_event_config = &mma8452_read_event_config,
144528e34278SMartin Fuzzey 	.write_event_config = &mma8452_write_event_config,
14462a17698cSMartin Fuzzey 	.debugfs_reg_access = &mma8452_reg_access_dbg,
1447c7eeea93SPeter Meerwald };
1448c7eeea93SPeter Meerwald 
1449c7eeea93SPeter Meerwald static const unsigned long mma8452_scan_masks[] = {0x7, 0};
1450c7eeea93SPeter Meerwald 
1451ae6d9ce0SMartin Fuzzey static int mma8452_data_rdy_trigger_set_state(struct iio_trigger *trig,
1452ae6d9ce0SMartin Fuzzey 					      bool state)
1453ae6d9ce0SMartin Fuzzey {
1454ae6d9ce0SMartin Fuzzey 	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
1455ae6d9ce0SMartin Fuzzey 	struct mma8452_data *data = iio_priv(indio_dev);
145696c0cb2bSMartin Kepplinger 	int reg, ret;
145796c0cb2bSMartin Kepplinger 
145896c0cb2bSMartin Kepplinger 	ret = mma8452_set_runtime_pm_state(data->client, state);
145996c0cb2bSMartin Kepplinger 	if (ret)
146096c0cb2bSMartin Kepplinger 		return ret;
1461ae6d9ce0SMartin Fuzzey 
1462ae6d9ce0SMartin Fuzzey 	reg = i2c_smbus_read_byte_data(data->client, MMA8452_CTRL_REG4);
1463ae6d9ce0SMartin Fuzzey 	if (reg < 0)
1464ae6d9ce0SMartin Fuzzey 		return reg;
1465ae6d9ce0SMartin Fuzzey 
1466ae6d9ce0SMartin Fuzzey 	if (state)
1467ae6d9ce0SMartin Fuzzey 		reg |= MMA8452_INT_DRDY;
1468ae6d9ce0SMartin Fuzzey 	else
1469ae6d9ce0SMartin Fuzzey 		reg &= ~MMA8452_INT_DRDY;
1470ae6d9ce0SMartin Fuzzey 
1471ae6d9ce0SMartin Fuzzey 	return mma8452_change_config(data, MMA8452_CTRL_REG4, reg);
1472ae6d9ce0SMartin Fuzzey }
1473ae6d9ce0SMartin Fuzzey 
1474ae6d9ce0SMartin Fuzzey static const struct iio_trigger_ops mma8452_trigger_ops = {
1475ae6d9ce0SMartin Fuzzey 	.set_trigger_state = mma8452_data_rdy_trigger_set_state,
147619808e04SLars-Peter Clausen 	.validate_device = iio_trigger_validate_own_device,
1477ae6d9ce0SMartin Fuzzey };
1478ae6d9ce0SMartin Fuzzey 
1479ae6d9ce0SMartin Fuzzey static int mma8452_trigger_setup(struct iio_dev *indio_dev)
1480ae6d9ce0SMartin Fuzzey {
1481ae6d9ce0SMartin Fuzzey 	struct mma8452_data *data = iio_priv(indio_dev);
1482ae6d9ce0SMartin Fuzzey 	struct iio_trigger *trig;
1483ae6d9ce0SMartin Fuzzey 	int ret;
1484ae6d9ce0SMartin Fuzzey 
1485ae6d9ce0SMartin Fuzzey 	trig = devm_iio_trigger_alloc(&data->client->dev, "%s-dev%d",
1486ae6d9ce0SMartin Fuzzey 				      indio_dev->name,
148715ea2878SJonathan Cameron 				      iio_device_id(indio_dev));
1488ae6d9ce0SMartin Fuzzey 	if (!trig)
1489ae6d9ce0SMartin Fuzzey 		return -ENOMEM;
1490ae6d9ce0SMartin Fuzzey 
1491ae6d9ce0SMartin Fuzzey 	trig->ops = &mma8452_trigger_ops;
1492ae6d9ce0SMartin Fuzzey 	iio_trigger_set_drvdata(trig, indio_dev);
1493ae6d9ce0SMartin Fuzzey 
1494ae6d9ce0SMartin Fuzzey 	ret = iio_trigger_register(trig);
1495ae6d9ce0SMartin Fuzzey 	if (ret)
1496ae6d9ce0SMartin Fuzzey 		return ret;
1497ae6d9ce0SMartin Fuzzey 
1498cd008223SLars-Peter Clausen 	indio_dev->trig = iio_trigger_get(trig);
1499686027fbSHartmut Knaack 
1500ae6d9ce0SMartin Fuzzey 	return 0;
1501ae6d9ce0SMartin Fuzzey }
1502ae6d9ce0SMartin Fuzzey 
1503ae6d9ce0SMartin Fuzzey static void mma8452_trigger_cleanup(struct iio_dev *indio_dev)
1504ae6d9ce0SMartin Fuzzey {
1505ae6d9ce0SMartin Fuzzey 	if (indio_dev->trig)
1506ae6d9ce0SMartin Fuzzey 		iio_trigger_unregister(indio_dev->trig);
1507ae6d9ce0SMartin Fuzzey }
1508ae6d9ce0SMartin Fuzzey 
1509ecabae71SMartin Fuzzey static int mma8452_reset(struct i2c_client *client)
1510ecabae71SMartin Fuzzey {
1511ecabae71SMartin Fuzzey 	int i;
1512ecabae71SMartin Fuzzey 	int ret;
1513ecabae71SMartin Fuzzey 
1514bf745142SHaibo Chen 	/*
1515bf745142SHaibo Chen 	 * Find on fxls8471, after config reset bit, it reset immediately,
1516bf745142SHaibo Chen 	 * and will not give ACK, so here do not check the return value.
1517bf745142SHaibo Chen 	 * The following code will read the reset register, and check whether
1518bf745142SHaibo Chen 	 * this reset works.
1519bf745142SHaibo Chen 	 */
1520bf745142SHaibo Chen 	i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG2,
1521ecabae71SMartin Fuzzey 					MMA8452_CTRL_REG2_RST);
1522ecabae71SMartin Fuzzey 
1523ecabae71SMartin Fuzzey 	for (i = 0; i < 10; i++) {
1524ecabae71SMartin Fuzzey 		usleep_range(100, 200);
1525ecabae71SMartin Fuzzey 		ret = i2c_smbus_read_byte_data(client, MMA8452_CTRL_REG2);
1526ecabae71SMartin Fuzzey 		if (ret == -EIO)
1527ecabae71SMartin Fuzzey 			continue; /* I2C comm reset */
1528ecabae71SMartin Fuzzey 		if (ret < 0)
1529ecabae71SMartin Fuzzey 			return ret;
1530ecabae71SMartin Fuzzey 		if (!(ret & MMA8452_CTRL_REG2_RST))
1531ecabae71SMartin Fuzzey 			return 0;
1532ecabae71SMartin Fuzzey 	}
1533ecabae71SMartin Fuzzey 
1534ecabae71SMartin Fuzzey 	return -ETIMEDOUT;
1535ecabae71SMartin Fuzzey }
1536ecabae71SMartin Fuzzey 
1537c3cdd6e4SMartin Kepplinger static const struct of_device_id mma8452_dt_ids[] = {
1538244a93f6SMartin Kepplinger 	{ .compatible = "fsl,mma8451", .data = &mma_chip_info_table[mma8451] },
1539c3cdd6e4SMartin Kepplinger 	{ .compatible = "fsl,mma8452", .data = &mma_chip_info_table[mma8452] },
1540c5ea1b58SMartin Kepplinger 	{ .compatible = "fsl,mma8453", .data = &mma_chip_info_table[mma8453] },
1541417e008bSMartin Kepplinger 	{ .compatible = "fsl,mma8652", .data = &mma_chip_info_table[mma8652] },
1542417e008bSMartin Kepplinger 	{ .compatible = "fsl,mma8653", .data = &mma_chip_info_table[mma8653] },
1543e8731180SMartin Kepplinger 	{ .compatible = "fsl,fxls8471", .data = &mma_chip_info_table[fxls8471] },
1544c3cdd6e4SMartin Kepplinger 	{ }
1545c3cdd6e4SMartin Kepplinger };
1546c3cdd6e4SMartin Kepplinger MODULE_DEVICE_TABLE(of, mma8452_dt_ids);
1547c3cdd6e4SMartin Kepplinger 
1548c7eeea93SPeter Meerwald static int mma8452_probe(struct i2c_client *client,
1549c7eeea93SPeter Meerwald 			 const struct i2c_device_id *id)
1550c7eeea93SPeter Meerwald {
1551c7eeea93SPeter Meerwald 	struct mma8452_data *data;
1552c7eeea93SPeter Meerwald 	struct iio_dev *indio_dev;
1553c7eeea93SPeter Meerwald 	int ret;
1554c3cdd6e4SMartin Kepplinger 
1555c7eeea93SPeter Meerwald 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
1556c7eeea93SPeter Meerwald 	if (!indio_dev)
1557c7eeea93SPeter Meerwald 		return -ENOMEM;
1558c7eeea93SPeter Meerwald 
1559c7eeea93SPeter Meerwald 	data = iio_priv(indio_dev);
1560c7eeea93SPeter Meerwald 	data->client = client;
1561c7eeea93SPeter Meerwald 	mutex_init(&data->lock);
1562a47ac019SHans de Goede 
1563a47ac019SHans de Goede 	data->chip_info = device_get_match_data(&client->dev);
1564fe188949SHaibo Chen 	if (!data->chip_info) {
1565fe188949SHaibo Chen 		if (id) {
1566a47ac019SHans de Goede 			data->chip_info = &mma_chip_info_table[id->driver_data];
1567a47ac019SHans de Goede 		} else {
1568a47ac019SHans de Goede 			dev_err(&client->dev, "unknown device model\n");
1569a47ac019SHans de Goede 			return -ENODEV;
1570a47ac019SHans de Goede 		}
1571fe188949SHaibo Chen 	}
1572c3cdd6e4SMartin Kepplinger 
1573b863f2e3SHans de Goede 	ret = iio_read_mount_matrix(&client->dev, &data->orientation);
1574b863f2e3SHans de Goede 	if (ret)
1575b863f2e3SHans de Goede 		return ret;
1576b863f2e3SHans de Goede 
1577f6ff49b8SAnson Huang 	data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
1578c8b9a023SKrzysztof Kozlowski 	if (IS_ERR(data->vdd_reg))
1579c8b9a023SKrzysztof Kozlowski 		return dev_err_probe(&client->dev, PTR_ERR(data->vdd_reg),
1580c8b9a023SKrzysztof Kozlowski 				     "failed to get VDD regulator!\n");
1581f6ff49b8SAnson Huang 
1582f6ff49b8SAnson Huang 	data->vddio_reg = devm_regulator_get(&client->dev, "vddio");
1583c8b9a023SKrzysztof Kozlowski 	if (IS_ERR(data->vddio_reg))
1584c8b9a023SKrzysztof Kozlowski 		return dev_err_probe(&client->dev, PTR_ERR(data->vddio_reg),
1585c8b9a023SKrzysztof Kozlowski 				     "failed to get VDDIO regulator!\n");
1586f6ff49b8SAnson Huang 
1587f6ff49b8SAnson Huang 	ret = regulator_enable(data->vdd_reg);
1588f6ff49b8SAnson Huang 	if (ret) {
1589f6ff49b8SAnson Huang 		dev_err(&client->dev, "failed to enable VDD regulator!\n");
1590f6ff49b8SAnson Huang 		return ret;
1591f6ff49b8SAnson Huang 	}
1592f6ff49b8SAnson Huang 
1593f6ff49b8SAnson Huang 	ret = regulator_enable(data->vddio_reg);
1594f6ff49b8SAnson Huang 	if (ret) {
1595f6ff49b8SAnson Huang 		dev_err(&client->dev, "failed to enable VDDIO regulator!\n");
1596f6ff49b8SAnson Huang 		goto disable_regulator_vdd;
1597f6ff49b8SAnson Huang 	}
1598f6ff49b8SAnson Huang 
1599417e008bSMartin Kepplinger 	ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I);
1600417e008bSMartin Kepplinger 	if (ret < 0)
1601f6ff49b8SAnson Huang 		goto disable_regulators;
1602417e008bSMartin Kepplinger 
1603417e008bSMartin Kepplinger 	switch (ret) {
1604244a93f6SMartin Kepplinger 	case MMA8451_DEVICE_ID:
1605417e008bSMartin Kepplinger 	case MMA8452_DEVICE_ID:
1606417e008bSMartin Kepplinger 	case MMA8453_DEVICE_ID:
1607417e008bSMartin Kepplinger 	case MMA8652_DEVICE_ID:
1608417e008bSMartin Kepplinger 	case MMA8653_DEVICE_ID:
1609e8731180SMartin Kepplinger 	case FXLS8471_DEVICE_ID:
1610417e008bSMartin Kepplinger 		if (ret == data->chip_info->chip_id)
1611417e008bSMartin Kepplinger 			break;
1612df561f66SGustavo A. R. Silva 		fallthrough;
1613417e008bSMartin Kepplinger 	default:
1614f6ff49b8SAnson Huang 		ret = -ENODEV;
1615f6ff49b8SAnson Huang 		goto disable_regulators;
1616417e008bSMartin Kepplinger 	}
1617417e008bSMartin Kepplinger 
1618c3cdd6e4SMartin Kepplinger 	dev_info(&client->dev, "registering %s accelerometer; ID 0x%x\n",
1619a47ac019SHans de Goede 		 data->chip_info->name, data->chip_info->chip_id);
1620c7eeea93SPeter Meerwald 
1621c7eeea93SPeter Meerwald 	i2c_set_clientdata(client, indio_dev);
1622c7eeea93SPeter Meerwald 	indio_dev->info = &mma8452_info;
1623a47ac019SHans de Goede 	indio_dev->name = data->chip_info->name;
1624c7eeea93SPeter Meerwald 	indio_dev->modes = INDIO_DIRECT_MODE;
1625c3cdd6e4SMartin Kepplinger 	indio_dev->channels = data->chip_info->channels;
1626c3cdd6e4SMartin Kepplinger 	indio_dev->num_channels = data->chip_info->num_channels;
1627c7eeea93SPeter Meerwald 	indio_dev->available_scan_masks = mma8452_scan_masks;
1628c7eeea93SPeter Meerwald 
1629ecabae71SMartin Fuzzey 	ret = mma8452_reset(client);
1630c7eeea93SPeter Meerwald 	if (ret < 0)
1631f6ff49b8SAnson Huang 		goto disable_regulators;
1632c7eeea93SPeter Meerwald 
1633c7eeea93SPeter Meerwald 	data->data_cfg = MMA8452_DATA_CFG_FS_2G;
1634c7eeea93SPeter Meerwald 	ret = i2c_smbus_write_byte_data(client, MMA8452_DATA_CFG,
1635c7eeea93SPeter Meerwald 					data->data_cfg);
1636c7eeea93SPeter Meerwald 	if (ret < 0)
1637f6ff49b8SAnson Huang 		goto disable_regulators;
1638c7eeea93SPeter Meerwald 
163928e34278SMartin Fuzzey 	/*
164028e34278SMartin Fuzzey 	 * By default set transient threshold to max to avoid events if
164128e34278SMartin Fuzzey 	 * enabling without configuring threshold.
164228e34278SMartin Fuzzey 	 */
164328e34278SMartin Fuzzey 	ret = i2c_smbus_write_byte_data(client, MMA8452_TRANSIENT_THS,
164428e34278SMartin Fuzzey 					MMA8452_TRANSIENT_THS_MASK);
164528e34278SMartin Fuzzey 	if (ret < 0)
1646f6ff49b8SAnson Huang 		goto disable_regulators;
164728e34278SMartin Fuzzey 
164828e34278SMartin Fuzzey 	if (client->irq) {
1649d2a3e093SMartin Kepplinger 		int irq2;
165028e34278SMartin Fuzzey 
1651d2a3e093SMartin Kepplinger 		irq2 = of_irq_get_byname(client->dev.of_node, "INT2");
1652d2a3e093SMartin Kepplinger 
1653d2a3e093SMartin Kepplinger 		if (irq2 == client->irq) {
1654d2a3e093SMartin Kepplinger 			dev_dbg(&client->dev, "using interrupt line INT2\n");
1655d2a3e093SMartin Kepplinger 		} else {
165628e34278SMartin Fuzzey 			ret = i2c_smbus_write_byte_data(client,
165728e34278SMartin Fuzzey 						MMA8452_CTRL_REG5,
1658605f72deSHarinath Nampally 						data->chip_info->all_events);
165928e34278SMartin Fuzzey 			if (ret < 0)
1660f6ff49b8SAnson Huang 				goto disable_regulators;
166128e34278SMartin Fuzzey 
1662d2a3e093SMartin Kepplinger 			dev_dbg(&client->dev, "using interrupt line INT1\n");
1663d2a3e093SMartin Kepplinger 		}
1664d2a3e093SMartin Kepplinger 
166528e34278SMartin Fuzzey 		ret = i2c_smbus_write_byte_data(client,
166628e34278SMartin Fuzzey 					MMA8452_CTRL_REG4,
1667605f72deSHarinath Nampally 					data->chip_info->enabled_events);
1668ae6d9ce0SMartin Fuzzey 		if (ret < 0)
1669f6ff49b8SAnson Huang 			goto disable_regulators;
1670ae6d9ce0SMartin Fuzzey 
1671ae6d9ce0SMartin Fuzzey 		ret = mma8452_trigger_setup(indio_dev);
167228e34278SMartin Fuzzey 		if (ret < 0)
1673f6ff49b8SAnson Huang 			goto disable_regulators;
167428e34278SMartin Fuzzey 	}
167528e34278SMartin Fuzzey 
1676ecabae71SMartin Fuzzey 	data->ctrl_reg1 = MMA8452_CTRL_ACTIVE |
1677ecabae71SMartin Fuzzey 			  (MMA8452_CTRL_DR_DEFAULT << MMA8452_CTRL_DR_SHIFT);
1678a45d1238SRichard Tresidder 
1679a45d1238SRichard Tresidder 	data->sleep_val = mma8452_calculate_sleep(data);
1680a45d1238SRichard Tresidder 
1681ecabae71SMartin Fuzzey 	ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1,
1682ecabae71SMartin Fuzzey 					data->ctrl_reg1);
1683ecabae71SMartin Fuzzey 	if (ret < 0)
1684ae6d9ce0SMartin Fuzzey 		goto trigger_cleanup;
1685ecabae71SMartin Fuzzey 
1686c7eeea93SPeter Meerwald 	ret = iio_triggered_buffer_setup(indio_dev, NULL,
1687c7eeea93SPeter Meerwald 					 mma8452_trigger_handler, NULL);
1688c7eeea93SPeter Meerwald 	if (ret < 0)
1689ae6d9ce0SMartin Fuzzey 		goto trigger_cleanup;
1690c7eeea93SPeter Meerwald 
169128e34278SMartin Fuzzey 	if (client->irq) {
169228e34278SMartin Fuzzey 		ret = devm_request_threaded_irq(&client->dev,
169328e34278SMartin Fuzzey 						client->irq,
169428e34278SMartin Fuzzey 						NULL, mma8452_interrupt,
169528e34278SMartin Fuzzey 						IRQF_TRIGGER_LOW | IRQF_ONESHOT,
169628e34278SMartin Fuzzey 						client->name, indio_dev);
169728e34278SMartin Fuzzey 		if (ret)
169828e34278SMartin Fuzzey 			goto buffer_cleanup;
169928e34278SMartin Fuzzey 	}
170028e34278SMartin Fuzzey 
170196c0cb2bSMartin Kepplinger 	ret = pm_runtime_set_active(&client->dev);
170296c0cb2bSMartin Kepplinger 	if (ret < 0)
170396c0cb2bSMartin Kepplinger 		goto buffer_cleanup;
170496c0cb2bSMartin Kepplinger 
170596c0cb2bSMartin Kepplinger 	pm_runtime_enable(&client->dev);
170696c0cb2bSMartin Kepplinger 	pm_runtime_set_autosuspend_delay(&client->dev,
170796c0cb2bSMartin Kepplinger 					 MMA8452_AUTO_SUSPEND_DELAY_MS);
170896c0cb2bSMartin Kepplinger 	pm_runtime_use_autosuspend(&client->dev);
170996c0cb2bSMartin Kepplinger 
1710c7eeea93SPeter Meerwald 	ret = iio_device_register(indio_dev);
1711c7eeea93SPeter Meerwald 	if (ret < 0)
1712c7eeea93SPeter Meerwald 		goto buffer_cleanup;
171328e34278SMartin Fuzzey 
17144b04266aSMartin Kepplinger 	ret = mma8452_set_freefall_mode(data, false);
17151a965d40SBijosh Thykkoottathil 	if (ret < 0)
1716d7369ae1SChuhong Yuan 		goto unregister_device;
17174b04266aSMartin Kepplinger 
1718c7eeea93SPeter Meerwald 	return 0;
1719c7eeea93SPeter Meerwald 
1720d7369ae1SChuhong Yuan unregister_device:
1721d7369ae1SChuhong Yuan 	iio_device_unregister(indio_dev);
1722d7369ae1SChuhong Yuan 
1723c7eeea93SPeter Meerwald buffer_cleanup:
1724c7eeea93SPeter Meerwald 	iio_triggered_buffer_cleanup(indio_dev);
1725ae6d9ce0SMartin Fuzzey 
1726ae6d9ce0SMartin Fuzzey trigger_cleanup:
1727ae6d9ce0SMartin Fuzzey 	mma8452_trigger_cleanup(indio_dev);
1728ae6d9ce0SMartin Fuzzey 
1729f6ff49b8SAnson Huang disable_regulators:
1730f6ff49b8SAnson Huang 	regulator_disable(data->vddio_reg);
1731f6ff49b8SAnson Huang 
1732f6ff49b8SAnson Huang disable_regulator_vdd:
1733f6ff49b8SAnson Huang 	regulator_disable(data->vdd_reg);
1734f6ff49b8SAnson Huang 
1735c7eeea93SPeter Meerwald 	return ret;
1736c7eeea93SPeter Meerwald }
1737c7eeea93SPeter Meerwald 
1738*ed5c2f5fSUwe Kleine-König static void mma8452_remove(struct i2c_client *client)
1739c7eeea93SPeter Meerwald {
1740c7eeea93SPeter Meerwald 	struct iio_dev *indio_dev = i2c_get_clientdata(client);
1741f6ff49b8SAnson Huang 	struct mma8452_data *data = iio_priv(indio_dev);
1742c7eeea93SPeter Meerwald 
1743c7eeea93SPeter Meerwald 	iio_device_unregister(indio_dev);
174496c0cb2bSMartin Kepplinger 
174596c0cb2bSMartin Kepplinger 	pm_runtime_disable(&client->dev);
174696c0cb2bSMartin Kepplinger 	pm_runtime_set_suspended(&client->dev);
174796c0cb2bSMartin Kepplinger 
1748c7eeea93SPeter Meerwald 	iio_triggered_buffer_cleanup(indio_dev);
1749ae6d9ce0SMartin Fuzzey 	mma8452_trigger_cleanup(indio_dev);
1750c7eeea93SPeter Meerwald 	mma8452_standby(iio_priv(indio_dev));
1751c7eeea93SPeter Meerwald 
1752f6ff49b8SAnson Huang 	regulator_disable(data->vddio_reg);
1753f6ff49b8SAnson Huang 	regulator_disable(data->vdd_reg);
1754c7eeea93SPeter Meerwald }
1755c7eeea93SPeter Meerwald 
175696c0cb2bSMartin Kepplinger #ifdef CONFIG_PM
175796c0cb2bSMartin Kepplinger static int mma8452_runtime_suspend(struct device *dev)
175896c0cb2bSMartin Kepplinger {
175996c0cb2bSMartin Kepplinger 	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
176096c0cb2bSMartin Kepplinger 	struct mma8452_data *data = iio_priv(indio_dev);
176196c0cb2bSMartin Kepplinger 	int ret;
176296c0cb2bSMartin Kepplinger 
176396c0cb2bSMartin Kepplinger 	mutex_lock(&data->lock);
176496c0cb2bSMartin Kepplinger 	ret = mma8452_standby(data);
176596c0cb2bSMartin Kepplinger 	mutex_unlock(&data->lock);
176696c0cb2bSMartin Kepplinger 	if (ret < 0) {
176796c0cb2bSMartin Kepplinger 		dev_err(&data->client->dev, "powering off device failed\n");
176896c0cb2bSMartin Kepplinger 		return -EAGAIN;
176996c0cb2bSMartin Kepplinger 	}
177096c0cb2bSMartin Kepplinger 
1771f6ff49b8SAnson Huang 	ret = regulator_disable(data->vddio_reg);
1772f6ff49b8SAnson Huang 	if (ret) {
1773f6ff49b8SAnson Huang 		dev_err(dev, "failed to disable VDDIO regulator\n");
1774f6ff49b8SAnson Huang 		return ret;
1775f6ff49b8SAnson Huang 	}
1776f6ff49b8SAnson Huang 
1777f6ff49b8SAnson Huang 	ret = regulator_disable(data->vdd_reg);
1778f6ff49b8SAnson Huang 	if (ret) {
1779f6ff49b8SAnson Huang 		dev_err(dev, "failed to disable VDD regulator\n");
1780f6ff49b8SAnson Huang 		return ret;
1781f6ff49b8SAnson Huang 	}
1782f6ff49b8SAnson Huang 
178396c0cb2bSMartin Kepplinger 	return 0;
178496c0cb2bSMartin Kepplinger }
178596c0cb2bSMartin Kepplinger 
178696c0cb2bSMartin Kepplinger static int mma8452_runtime_resume(struct device *dev)
178796c0cb2bSMartin Kepplinger {
178896c0cb2bSMartin Kepplinger 	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
178996c0cb2bSMartin Kepplinger 	struct mma8452_data *data = iio_priv(indio_dev);
179096c0cb2bSMartin Kepplinger 	int ret, sleep_val;
179196c0cb2bSMartin Kepplinger 
1792f6ff49b8SAnson Huang 	ret = regulator_enable(data->vdd_reg);
1793f6ff49b8SAnson Huang 	if (ret) {
1794f6ff49b8SAnson Huang 		dev_err(dev, "failed to enable VDD regulator\n");
1795f6ff49b8SAnson Huang 		return ret;
1796f6ff49b8SAnson Huang 	}
1797f6ff49b8SAnson Huang 
1798f6ff49b8SAnson Huang 	ret = regulator_enable(data->vddio_reg);
1799f6ff49b8SAnson Huang 	if (ret) {
1800f6ff49b8SAnson Huang 		dev_err(dev, "failed to enable VDDIO regulator\n");
1801f6ff49b8SAnson Huang 		regulator_disable(data->vdd_reg);
1802f6ff49b8SAnson Huang 		return ret;
1803f6ff49b8SAnson Huang 	}
1804f6ff49b8SAnson Huang 
180596c0cb2bSMartin Kepplinger 	ret = mma8452_active(data);
180696c0cb2bSMartin Kepplinger 	if (ret < 0)
1807f6ff49b8SAnson Huang 		goto runtime_resume_failed;
180896c0cb2bSMartin Kepplinger 
180996c0cb2bSMartin Kepplinger 	ret = mma8452_get_odr_index(data);
181096c0cb2bSMartin Kepplinger 	sleep_val = 1000 / mma8452_samp_freq[ret][0];
181196c0cb2bSMartin Kepplinger 	if (sleep_val < 20)
181296c0cb2bSMartin Kepplinger 		usleep_range(sleep_val * 1000, 20000);
181396c0cb2bSMartin Kepplinger 	else
181496c0cb2bSMartin Kepplinger 		msleep_interruptible(sleep_val);
181596c0cb2bSMartin Kepplinger 
181696c0cb2bSMartin Kepplinger 	return 0;
181796c0cb2bSMartin Kepplinger 
1818f6ff49b8SAnson Huang runtime_resume_failed:
1819f6ff49b8SAnson Huang 	regulator_disable(data->vddio_reg);
1820f6ff49b8SAnson Huang 	regulator_disable(data->vdd_reg);
1821c7eeea93SPeter Meerwald 
1822f6ff49b8SAnson Huang 	return ret;
1823c7eeea93SPeter Meerwald }
1824c7eeea93SPeter Meerwald #endif
1825c7eeea93SPeter Meerwald 
182696c0cb2bSMartin Kepplinger static const struct dev_pm_ops mma8452_pm_ops = {
1827f6ff49b8SAnson Huang 	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
182896c0cb2bSMartin Kepplinger 	SET_RUNTIME_PM_OPS(mma8452_runtime_suspend,
182996c0cb2bSMartin Kepplinger 			   mma8452_runtime_resume, NULL)
183096c0cb2bSMartin Kepplinger };
183196c0cb2bSMartin Kepplinger 
1832c7eeea93SPeter Meerwald static const struct i2c_device_id mma8452_id[] = {
1833ddb851afSMartin Kepplinger 	{ "mma8451", mma8451 },
1834c3cdd6e4SMartin Kepplinger 	{ "mma8452", mma8452 },
1835c5ea1b58SMartin Kepplinger 	{ "mma8453", mma8453 },
1836417e008bSMartin Kepplinger 	{ "mma8652", mma8652 },
1837417e008bSMartin Kepplinger 	{ "mma8653", mma8653 },
1838e8731180SMartin Kepplinger 	{ "fxls8471", fxls8471 },
1839c7eeea93SPeter Meerwald 	{ }
1840c7eeea93SPeter Meerwald };
1841c7eeea93SPeter Meerwald MODULE_DEVICE_TABLE(i2c, mma8452_id);
1842c7eeea93SPeter Meerwald 
1843c7eeea93SPeter Meerwald static struct i2c_driver mma8452_driver = {
1844c7eeea93SPeter Meerwald 	.driver = {
1845c7eeea93SPeter Meerwald 		.name	= "mma8452",
1846a47ac019SHans de Goede 		.of_match_table = mma8452_dt_ids,
184796c0cb2bSMartin Kepplinger 		.pm	= &mma8452_pm_ops,
1848c7eeea93SPeter Meerwald 	},
1849c7eeea93SPeter Meerwald 	.probe = mma8452_probe,
1850c7eeea93SPeter Meerwald 	.remove = mma8452_remove,
1851c7eeea93SPeter Meerwald 	.id_table = mma8452_id,
1852c7eeea93SPeter Meerwald };
1853c7eeea93SPeter Meerwald module_i2c_driver(mma8452_driver);
1854c7eeea93SPeter Meerwald 
1855c7eeea93SPeter Meerwald MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
1856f26ab1aaSMartin Kepplinger MODULE_DESCRIPTION("Freescale / NXP MMA8452 accelerometer driver");
1857c7eeea93SPeter Meerwald MODULE_LICENSE("GPL");
1858