xref: /linux/drivers/iio/accel/mma8452.c (revision d7369ae1f4d7cffa7574d15e1f787dcca184c49d)
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;
107c7eeea93SPeter Meerwald 	u8 ctrl_reg1;
108c7eeea93SPeter Meerwald 	u8 data_cfg;
109c3cdd6e4SMartin Kepplinger 	const struct mma_chip_info *chip_info;
110a45d1238SRichard Tresidder 	int sleep_val;
111f6ff49b8SAnson Huang 	struct regulator *vdd_reg;
112f6ff49b8SAnson Huang 	struct regulator *vddio_reg;
113c3cdd6e4SMartin Kepplinger };
114c3cdd6e4SMartin Kepplinger 
115c3cdd6e4SMartin Kepplinger  /**
116605f72deSHarinath Nampally   * struct mma8452_event_regs - chip specific data related to events
117c3cdd6e4SMartin Kepplinger   * @ev_cfg:			event config register address
118c3cdd6e4SMartin Kepplinger   * @ev_cfg_ele:			latch bit in event config register
119c3cdd6e4SMartin Kepplinger   * @ev_cfg_chan_shift:		number of the bit to enable events in X
120c3cdd6e4SMartin Kepplinger   *				direction; in event config register
121c3cdd6e4SMartin Kepplinger   * @ev_src:			event source register address
122c3cdd6e4SMartin Kepplinger   * @ev_ths:			event threshold register address
123c3cdd6e4SMartin Kepplinger   * @ev_ths_mask:		mask for the threshold value
124c3cdd6e4SMartin Kepplinger   * @ev_count:			event count (period) register address
125c3cdd6e4SMartin Kepplinger   *
126c3cdd6e4SMartin Kepplinger   * Since not all chips supported by the driver support comparing high pass
127c3cdd6e4SMartin Kepplinger   * filtered data for events (interrupts), different interrupt sources are
128c3cdd6e4SMartin Kepplinger   * used for different chips and the relevant registers are included here.
129c3cdd6e4SMartin Kepplinger   */
130605f72deSHarinath Nampally struct mma8452_event_regs {
131605f72deSHarinath Nampally 		u8 ev_cfg;
132605f72deSHarinath Nampally 		u8 ev_cfg_ele;
133605f72deSHarinath Nampally 		u8 ev_cfg_chan_shift;
134605f72deSHarinath Nampally 		u8 ev_src;
135605f72deSHarinath Nampally 		u8 ev_ths;
136605f72deSHarinath Nampally 		u8 ev_ths_mask;
137605f72deSHarinath Nampally 		u8 ev_count;
138605f72deSHarinath Nampally };
139605f72deSHarinath Nampally 
140a654c062SHarinath Nampally static const struct mma8452_event_regs ff_mt_ev_regs = {
141605f72deSHarinath Nampally 		.ev_cfg = MMA8452_FF_MT_CFG,
142605f72deSHarinath Nampally 		.ev_cfg_ele = MMA8452_FF_MT_CFG_ELE,
143605f72deSHarinath Nampally 		.ev_cfg_chan_shift = MMA8452_FF_MT_CHAN_SHIFT,
144605f72deSHarinath Nampally 		.ev_src = MMA8452_FF_MT_SRC,
145605f72deSHarinath Nampally 		.ev_ths = MMA8452_FF_MT_THS,
146605f72deSHarinath Nampally 		.ev_ths_mask = MMA8452_FF_MT_THS_MASK,
147605f72deSHarinath Nampally 		.ev_count = MMA8452_FF_MT_COUNT
148605f72deSHarinath Nampally };
149605f72deSHarinath Nampally 
150a654c062SHarinath Nampally static const struct mma8452_event_regs trans_ev_regs = {
151605f72deSHarinath Nampally 		.ev_cfg = MMA8452_TRANSIENT_CFG,
152605f72deSHarinath Nampally 		.ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE,
153605f72deSHarinath Nampally 		.ev_cfg_chan_shift = MMA8452_TRANSIENT_CHAN_SHIFT,
154605f72deSHarinath Nampally 		.ev_src = MMA8452_TRANSIENT_SRC,
155605f72deSHarinath Nampally 		.ev_ths = MMA8452_TRANSIENT_THS,
156605f72deSHarinath Nampally 		.ev_ths_mask = MMA8452_TRANSIENT_THS_MASK,
157605f72deSHarinath Nampally 		.ev_count = MMA8452_TRANSIENT_COUNT,
158605f72deSHarinath Nampally };
159605f72deSHarinath Nampally 
160605f72deSHarinath Nampally /**
161605f72deSHarinath Nampally  * struct mma_chip_info - chip specific data
162605f72deSHarinath Nampally  * @chip_id:			WHO_AM_I register's value
163605f72deSHarinath Nampally  * @channels:			struct iio_chan_spec matching the device's
164605f72deSHarinath Nampally  *				capabilities
165605f72deSHarinath Nampally  * @num_channels:		number of channels
166605f72deSHarinath Nampally  * @mma_scales:			scale factors for converting register values
167605f72deSHarinath Nampally  *				to m/s^2; 3 modes: 2g, 4g, 8g; 2 integers
168605f72deSHarinath Nampally  *				per mode: m/s^2 and micro m/s^2
169605f72deSHarinath Nampally  * @all_events:			all events supported by this chip
170605f72deSHarinath Nampally  * @enabled_events:		event flags enabled and handled by this driver
171605f72deSHarinath Nampally  */
172c3cdd6e4SMartin Kepplinger struct mma_chip_info {
173c3cdd6e4SMartin Kepplinger 	u8 chip_id;
174c3cdd6e4SMartin Kepplinger 	const struct iio_chan_spec *channels;
175c3cdd6e4SMartin Kepplinger 	int num_channels;
176c3cdd6e4SMartin Kepplinger 	const int mma_scales[3][2];
177605f72deSHarinath Nampally 	int all_events;
178605f72deSHarinath Nampally 	int enabled_events;
179c7eeea93SPeter Meerwald };
180c7eeea93SPeter Meerwald 
181e60378c1SMartin Kepplinger enum {
182e60378c1SMartin Kepplinger 	idx_x,
183e60378c1SMartin Kepplinger 	idx_y,
184e60378c1SMartin Kepplinger 	idx_z,
185e60378c1SMartin Kepplinger 	idx_ts,
186e60378c1SMartin Kepplinger };
187e60378c1SMartin Kepplinger 
188c7eeea93SPeter Meerwald static int mma8452_drdy(struct mma8452_data *data)
189c7eeea93SPeter Meerwald {
190c7eeea93SPeter Meerwald 	int tries = 150;
191c7eeea93SPeter Meerwald 
192c7eeea93SPeter Meerwald 	while (tries-- > 0) {
193c7eeea93SPeter Meerwald 		int ret = i2c_smbus_read_byte_data(data->client,
194c7eeea93SPeter Meerwald 			MMA8452_STATUS);
195c7eeea93SPeter Meerwald 		if (ret < 0)
196c7eeea93SPeter Meerwald 			return ret;
197c7eeea93SPeter Meerwald 		if ((ret & MMA8452_STATUS_DRDY) == MMA8452_STATUS_DRDY)
198c7eeea93SPeter Meerwald 			return 0;
199686027fbSHartmut Knaack 
200a45d1238SRichard Tresidder 		if (data->sleep_val <= 20)
201a45d1238SRichard Tresidder 			usleep_range(data->sleep_val * 250,
202a45d1238SRichard Tresidder 				     data->sleep_val * 500);
203a45d1238SRichard Tresidder 		else
204c7eeea93SPeter Meerwald 			msleep(20);
205c7eeea93SPeter Meerwald 	}
206c7eeea93SPeter Meerwald 
207c7eeea93SPeter Meerwald 	dev_err(&data->client->dev, "data not ready\n");
208686027fbSHartmut Knaack 
209c7eeea93SPeter Meerwald 	return -EIO;
210c7eeea93SPeter Meerwald }
211c7eeea93SPeter Meerwald 
21296c0cb2bSMartin Kepplinger static int mma8452_set_runtime_pm_state(struct i2c_client *client, bool on)
21396c0cb2bSMartin Kepplinger {
21496c0cb2bSMartin Kepplinger #ifdef CONFIG_PM
21596c0cb2bSMartin Kepplinger 	int ret;
21696c0cb2bSMartin Kepplinger 
21796c0cb2bSMartin Kepplinger 	if (on) {
21896c0cb2bSMartin Kepplinger 		ret = pm_runtime_get_sync(&client->dev);
21996c0cb2bSMartin Kepplinger 	} else {
22096c0cb2bSMartin Kepplinger 		pm_runtime_mark_last_busy(&client->dev);
22196c0cb2bSMartin Kepplinger 		ret = pm_runtime_put_autosuspend(&client->dev);
22296c0cb2bSMartin Kepplinger 	}
22396c0cb2bSMartin Kepplinger 
22496c0cb2bSMartin Kepplinger 	if (ret < 0) {
22596c0cb2bSMartin Kepplinger 		dev_err(&client->dev,
22696c0cb2bSMartin Kepplinger 			"failed to change power state to %d\n", on);
22796c0cb2bSMartin Kepplinger 		if (on)
22896c0cb2bSMartin Kepplinger 			pm_runtime_put_noidle(&client->dev);
22996c0cb2bSMartin Kepplinger 
23096c0cb2bSMartin Kepplinger 		return ret;
23196c0cb2bSMartin Kepplinger 	}
23296c0cb2bSMartin Kepplinger #endif
23396c0cb2bSMartin Kepplinger 
23496c0cb2bSMartin Kepplinger 	return 0;
23596c0cb2bSMartin Kepplinger }
23696c0cb2bSMartin Kepplinger 
237c7eeea93SPeter Meerwald static int mma8452_read(struct mma8452_data *data, __be16 buf[3])
238c7eeea93SPeter Meerwald {
239c7eeea93SPeter Meerwald 	int ret = mma8452_drdy(data);
240686027fbSHartmut Knaack 
241c7eeea93SPeter Meerwald 	if (ret < 0)
242c7eeea93SPeter Meerwald 		return ret;
243686027fbSHartmut Knaack 
24496c0cb2bSMartin Kepplinger 	ret = mma8452_set_runtime_pm_state(data->client, true);
24596c0cb2bSMartin Kepplinger 	if (ret)
24696c0cb2bSMartin Kepplinger 		return ret;
24796c0cb2bSMartin Kepplinger 
24896c0cb2bSMartin Kepplinger 	ret = i2c_smbus_read_i2c_block_data(data->client, MMA8452_OUT_X,
249686027fbSHartmut Knaack 					    3 * sizeof(__be16), (u8 *)buf);
25096c0cb2bSMartin Kepplinger 
25196c0cb2bSMartin Kepplinger 	ret = mma8452_set_runtime_pm_state(data->client, false);
25296c0cb2bSMartin Kepplinger 
25396c0cb2bSMartin Kepplinger 	return ret;
254c7eeea93SPeter Meerwald }
255c7eeea93SPeter Meerwald 
256686027fbSHartmut Knaack static ssize_t mma8452_show_int_plus_micros(char *buf, const int (*vals)[2],
257686027fbSHartmut Knaack 					    int n)
258c7eeea93SPeter Meerwald {
259c7eeea93SPeter Meerwald 	size_t len = 0;
260c7eeea93SPeter Meerwald 
261c7eeea93SPeter Meerwald 	while (n-- > 0)
262686027fbSHartmut Knaack 		len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ",
263686027fbSHartmut Knaack 				 vals[n][0], vals[n][1]);
264c7eeea93SPeter Meerwald 
265c7eeea93SPeter Meerwald 	/* replace trailing space by newline */
266c7eeea93SPeter Meerwald 	buf[len - 1] = '\n';
267c7eeea93SPeter Meerwald 
268c7eeea93SPeter Meerwald 	return len;
269c7eeea93SPeter Meerwald }
270c7eeea93SPeter Meerwald 
271c7eeea93SPeter Meerwald static int mma8452_get_int_plus_micros_index(const int (*vals)[2], int n,
272c7eeea93SPeter Meerwald 					     int val, int val2)
273c7eeea93SPeter Meerwald {
274c7eeea93SPeter Meerwald 	while (n-- > 0)
275c7eeea93SPeter Meerwald 		if (val == vals[n][0] && val2 == vals[n][1])
276c7eeea93SPeter Meerwald 			return n;
277c7eeea93SPeter Meerwald 
278c7eeea93SPeter Meerwald 	return -EINVAL;
279c7eeea93SPeter Meerwald }
280c7eeea93SPeter Meerwald 
28132b28076SMartin Kepplinger static unsigned int mma8452_get_odr_index(struct mma8452_data *data)
2825dbbd19fSMartin Fuzzey {
2835dbbd19fSMartin Fuzzey 	return (data->ctrl_reg1 & MMA8452_CTRL_DR_MASK) >>
2845dbbd19fSMartin Fuzzey 			MMA8452_CTRL_DR_SHIFT;
2855dbbd19fSMartin Fuzzey }
2865dbbd19fSMartin Fuzzey 
287c7eeea93SPeter Meerwald static const int mma8452_samp_freq[8][2] = {
288c7eeea93SPeter Meerwald 	{800, 0}, {400, 0}, {200, 0}, {100, 0}, {50, 0}, {12, 500000},
289c7eeea93SPeter Meerwald 	{6, 250000}, {1, 560000}
290c7eeea93SPeter Meerwald };
291c7eeea93SPeter Meerwald 
292ed859fc1SMartin Kepplinger /* Datasheet table: step time "Relationship with the ODR" (sample frequency) */
293cc54a660SHarinath Nampally static const unsigned int mma8452_time_step_us[4][8] = {
294ed859fc1SMartin Kepplinger 	{ 1250, 2500, 5000, 10000, 20000, 20000, 20000, 20000 },  /* normal */
295ed859fc1SMartin Kepplinger 	{ 1250, 2500, 5000, 10000, 20000, 80000, 80000, 80000 },  /* l p l n */
296ed859fc1SMartin Kepplinger 	{ 1250, 2500, 2500, 2500, 2500, 2500, 2500, 2500 },	  /* high res*/
297ed859fc1SMartin Kepplinger 	{ 1250, 2500, 5000, 10000, 20000, 80000, 160000, 160000 } /* l p */
2985dbbd19fSMartin Fuzzey };
2995dbbd19fSMartin Fuzzey 
300ed859fc1SMartin Kepplinger /* Datasheet table "High-Pass Filter Cutoff Options" */
301ed859fc1SMartin Kepplinger static const int mma8452_hp_filter_cutoff[4][8][4][2] = {
302ed859fc1SMartin Kepplinger 	{ /* normal */
3031e79841aSMartin Fuzzey 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },		/* 800 Hz sample */
3041e79841aSMartin Fuzzey 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },		/* 400 Hz sample */
3051e79841aSMartin Fuzzey 	{ {8, 0}, {4, 0}, {2, 0}, {1, 0} },		/* 200 Hz sample */
3061e79841aSMartin Fuzzey 	{ {4, 0}, {2, 0}, {1, 0}, {0, 500000} },	/* 100 Hz sample */
3071e79841aSMartin Fuzzey 	{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} },	/* 50 Hz sample */
3081e79841aSMartin Fuzzey 	{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} },	/* 12.5 Hz sample */
3091e79841aSMartin Fuzzey 	{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} },	/* 6.25 Hz sample */
3101e79841aSMartin Fuzzey 	{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }	/* 1.56 Hz sample */
311ed859fc1SMartin Kepplinger 	},
312ed859fc1SMartin Kepplinger 	{ /* low noise low power */
313ed859fc1SMartin Kepplinger 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
314ed859fc1SMartin Kepplinger 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
315ed859fc1SMartin Kepplinger 	{ {8, 0}, {4, 0}, {2, 0}, {1, 0} },
316ed859fc1SMartin Kepplinger 	{ {4, 0}, {2, 0}, {1, 0}, {0, 500000} },
317ed859fc1SMartin Kepplinger 	{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} },
318ed859fc1SMartin Kepplinger 	{ {0, 500000}, {0, 250000}, {0, 125000}, {0, 063000} },
319ed859fc1SMartin Kepplinger 	{ {0, 500000}, {0, 250000}, {0, 125000}, {0, 063000} },
320ed859fc1SMartin Kepplinger 	{ {0, 500000}, {0, 250000}, {0, 125000}, {0, 063000} }
321ed859fc1SMartin Kepplinger 	},
322ed859fc1SMartin Kepplinger 	{ /* high resolution */
323ed859fc1SMartin Kepplinger 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
324ed859fc1SMartin Kepplinger 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
325ed859fc1SMartin Kepplinger 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
326ed859fc1SMartin Kepplinger 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
327ed859fc1SMartin Kepplinger 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
328ed859fc1SMartin Kepplinger 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
329ed859fc1SMartin Kepplinger 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
330ed859fc1SMartin Kepplinger 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} }
331ed859fc1SMartin Kepplinger 	},
332ed859fc1SMartin Kepplinger 	{ /* low power */
333ed859fc1SMartin Kepplinger 	{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
334ed859fc1SMartin Kepplinger 	{ {8, 0}, {4, 0}, {2, 0}, {1, 0} },
335ed859fc1SMartin Kepplinger 	{ {4, 0}, {2, 0}, {1, 0}, {0, 500000} },
336ed859fc1SMartin Kepplinger 	{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} },
337ed859fc1SMartin Kepplinger 	{ {1, 0}, {0, 500000}, {0, 250000}, {0, 125000} },
338ed859fc1SMartin Kepplinger 	{ {0, 250000}, {0, 125000}, {0, 063000}, {0, 031000} },
339ed859fc1SMartin Kepplinger 	{ {0, 250000}, {0, 125000}, {0, 063000}, {0, 031000} },
340ed859fc1SMartin Kepplinger 	{ {0, 250000}, {0, 125000}, {0, 063000}, {0, 031000} }
341ed859fc1SMartin Kepplinger 	}
3421e79841aSMartin Fuzzey };
3431e79841aSMartin Fuzzey 
344ed859fc1SMartin Kepplinger /* Datasheet table "MODS Oversampling modes averaging values at each ODR" */
345ed859fc1SMartin Kepplinger static const u16 mma8452_os_ratio[4][8] = {
346ed859fc1SMartin Kepplinger 	/* 800 Hz, 400 Hz, ... , 1.56 Hz */
347ed859fc1SMartin Kepplinger 	{ 2, 4, 4, 4, 4, 16, 32, 128 },		/* normal */
348ed859fc1SMartin Kepplinger 	{ 2, 4, 4, 4, 4, 4, 8, 32 },		/* low power low noise */
349ed859fc1SMartin Kepplinger 	{ 2, 4, 8, 16, 32, 128, 256, 1024 },	/* high resolution */
350ed859fc1SMartin Kepplinger 	{ 2, 2, 2, 2, 2, 2, 4, 16 }		/* low power */
351ed859fc1SMartin Kepplinger };
352ed859fc1SMartin Kepplinger 
353ed859fc1SMartin Kepplinger static int mma8452_get_power_mode(struct mma8452_data *data)
354ed859fc1SMartin Kepplinger {
355ed859fc1SMartin Kepplinger 	int reg;
356ed859fc1SMartin Kepplinger 
357ed859fc1SMartin Kepplinger 	reg = i2c_smbus_read_byte_data(data->client,
358ed859fc1SMartin Kepplinger 				       MMA8452_CTRL_REG2);
359ed859fc1SMartin Kepplinger 	if (reg < 0)
360ed859fc1SMartin Kepplinger 		return reg;
361ed859fc1SMartin Kepplinger 
362ed859fc1SMartin Kepplinger 	return ((reg & MMA8452_CTRL_REG2_MODS_MASK) >>
363ed859fc1SMartin Kepplinger 		MMA8452_CTRL_REG2_MODS_SHIFT);
364ed859fc1SMartin Kepplinger }
365ed859fc1SMartin Kepplinger 
366c7eeea93SPeter Meerwald static ssize_t mma8452_show_samp_freq_avail(struct device *dev,
367686027fbSHartmut Knaack 					    struct device_attribute *attr,
368686027fbSHartmut Knaack 					    char *buf)
369c7eeea93SPeter Meerwald {
370c7eeea93SPeter Meerwald 	return mma8452_show_int_plus_micros(buf, mma8452_samp_freq,
371c7eeea93SPeter Meerwald 					    ARRAY_SIZE(mma8452_samp_freq));
372c7eeea93SPeter Meerwald }
373c7eeea93SPeter Meerwald 
374c7eeea93SPeter Meerwald static ssize_t mma8452_show_scale_avail(struct device *dev,
375686027fbSHartmut Knaack 					struct device_attribute *attr,
376686027fbSHartmut Knaack 					char *buf)
377c7eeea93SPeter Meerwald {
378c3cdd6e4SMartin Kepplinger 	struct mma8452_data *data = iio_priv(i2c_get_clientdata(
379c3cdd6e4SMartin Kepplinger 					     to_i2c_client(dev)));
380c3cdd6e4SMartin Kepplinger 
381c3cdd6e4SMartin Kepplinger 	return mma8452_show_int_plus_micros(buf, data->chip_info->mma_scales,
382c3cdd6e4SMartin Kepplinger 		ARRAY_SIZE(data->chip_info->mma_scales));
383c7eeea93SPeter Meerwald }
384c7eeea93SPeter Meerwald 
3851e79841aSMartin Fuzzey static ssize_t mma8452_show_hp_cutoff_avail(struct device *dev,
3861e79841aSMartin Fuzzey 					    struct device_attribute *attr,
3871e79841aSMartin Fuzzey 					    char *buf)
3881e79841aSMartin Fuzzey {
3891e79841aSMartin Fuzzey 	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
3901e79841aSMartin Fuzzey 	struct mma8452_data *data = iio_priv(indio_dev);
391ed859fc1SMartin Kepplinger 	int i, j;
3921e79841aSMartin Fuzzey 
393ed859fc1SMartin Kepplinger 	i = mma8452_get_odr_index(data);
394ed859fc1SMartin Kepplinger 	j = mma8452_get_power_mode(data);
395ed859fc1SMartin Kepplinger 	if (j < 0)
396ed859fc1SMartin Kepplinger 		return j;
397ed859fc1SMartin Kepplinger 
398ed859fc1SMartin Kepplinger 	return mma8452_show_int_plus_micros(buf, mma8452_hp_filter_cutoff[j][i],
399ed859fc1SMartin Kepplinger 		ARRAY_SIZE(mma8452_hp_filter_cutoff[0][0]));
400ed859fc1SMartin Kepplinger }
401ed859fc1SMartin Kepplinger 
402ed859fc1SMartin Kepplinger static ssize_t mma8452_show_os_ratio_avail(struct device *dev,
403ed859fc1SMartin Kepplinger 					   struct device_attribute *attr,
404ed859fc1SMartin Kepplinger 					   char *buf)
405ed859fc1SMartin Kepplinger {
406ed859fc1SMartin Kepplinger 	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
407ed859fc1SMartin Kepplinger 	struct mma8452_data *data = iio_priv(indio_dev);
408ed859fc1SMartin Kepplinger 	int i = mma8452_get_odr_index(data);
409ed859fc1SMartin Kepplinger 	int j;
410ed859fc1SMartin Kepplinger 	u16 val = 0;
411ed859fc1SMartin Kepplinger 	size_t len = 0;
412ed859fc1SMartin Kepplinger 
413ed859fc1SMartin Kepplinger 	for (j = 0; j < ARRAY_SIZE(mma8452_os_ratio); j++) {
414ed859fc1SMartin Kepplinger 		if (val == mma8452_os_ratio[j][i])
415ed859fc1SMartin Kepplinger 			continue;
416ed859fc1SMartin Kepplinger 
417ed859fc1SMartin Kepplinger 		val = mma8452_os_ratio[j][i];
418ed859fc1SMartin Kepplinger 
419ed859fc1SMartin Kepplinger 		len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", val);
420ed859fc1SMartin Kepplinger 	}
421ed859fc1SMartin Kepplinger 	buf[len - 1] = '\n';
422ed859fc1SMartin Kepplinger 
423ed859fc1SMartin Kepplinger 	return len;
4241e79841aSMartin Fuzzey }
4251e79841aSMartin Fuzzey 
426c7eeea93SPeter Meerwald static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mma8452_show_samp_freq_avail);
427cd327b00SHarinath Nampally static IIO_DEVICE_ATTR(in_accel_scale_available, 0444,
428c7eeea93SPeter Meerwald 		       mma8452_show_scale_avail, NULL, 0);
4291e79841aSMartin Fuzzey static IIO_DEVICE_ATTR(in_accel_filter_high_pass_3db_frequency_available,
430cd327b00SHarinath Nampally 		       0444, mma8452_show_hp_cutoff_avail, NULL, 0);
431cd327b00SHarinath Nampally static IIO_DEVICE_ATTR(in_accel_oversampling_ratio_available, 0444,
432ed859fc1SMartin Kepplinger 		       mma8452_show_os_ratio_avail, NULL, 0);
433c7eeea93SPeter Meerwald 
434c7eeea93SPeter Meerwald static int mma8452_get_samp_freq_index(struct mma8452_data *data,
435c7eeea93SPeter Meerwald 				       int val, int val2)
436c7eeea93SPeter Meerwald {
437c7eeea93SPeter Meerwald 	return mma8452_get_int_plus_micros_index(mma8452_samp_freq,
438686027fbSHartmut Knaack 						 ARRAY_SIZE(mma8452_samp_freq),
439686027fbSHartmut Knaack 						 val, val2);
440c7eeea93SPeter Meerwald }
441c7eeea93SPeter Meerwald 
442686027fbSHartmut Knaack static int mma8452_get_scale_index(struct mma8452_data *data, int val, int val2)
443c7eeea93SPeter Meerwald {
444c3cdd6e4SMartin Kepplinger 	return mma8452_get_int_plus_micros_index(data->chip_info->mma_scales,
445c3cdd6e4SMartin Kepplinger 			ARRAY_SIZE(data->chip_info->mma_scales), val, val2);
446c7eeea93SPeter Meerwald }
447c7eeea93SPeter Meerwald 
4481e79841aSMartin Fuzzey static int mma8452_get_hp_filter_index(struct mma8452_data *data,
4491e79841aSMartin Fuzzey 				       int val, int val2)
4501e79841aSMartin Fuzzey {
451ed859fc1SMartin Kepplinger 	int i, j;
4521e79841aSMartin Fuzzey 
453ed859fc1SMartin Kepplinger 	i = mma8452_get_odr_index(data);
454ed859fc1SMartin Kepplinger 	j = mma8452_get_power_mode(data);
455ed859fc1SMartin Kepplinger 	if (j < 0)
456ed859fc1SMartin Kepplinger 		return j;
457ed859fc1SMartin Kepplinger 
458ed859fc1SMartin Kepplinger 	return mma8452_get_int_plus_micros_index(mma8452_hp_filter_cutoff[j][i],
459ed859fc1SMartin Kepplinger 		ARRAY_SIZE(mma8452_hp_filter_cutoff[0][0]), val, val2);
4601e79841aSMartin Fuzzey }
4611e79841aSMartin Fuzzey 
4621e79841aSMartin Fuzzey static int mma8452_read_hp_filter(struct mma8452_data *data, int *hz, int *uHz)
4631e79841aSMartin Fuzzey {
464ed859fc1SMartin Kepplinger 	int j, i, ret;
4651e79841aSMartin Fuzzey 
4661e79841aSMartin Fuzzey 	ret = i2c_smbus_read_byte_data(data->client, MMA8452_HP_FILTER_CUTOFF);
4671e79841aSMartin Fuzzey 	if (ret < 0)
4681e79841aSMartin Fuzzey 		return ret;
4691e79841aSMartin Fuzzey 
4701e79841aSMartin Fuzzey 	i = mma8452_get_odr_index(data);
471ed859fc1SMartin Kepplinger 	j = mma8452_get_power_mode(data);
472ed859fc1SMartin Kepplinger 	if (j < 0)
473ed859fc1SMartin Kepplinger 		return j;
474ed859fc1SMartin Kepplinger 
4751e79841aSMartin Fuzzey 	ret &= MMA8452_HP_FILTER_CUTOFF_SEL_MASK;
476ed859fc1SMartin Kepplinger 	*hz = mma8452_hp_filter_cutoff[j][i][ret][0];
477ed859fc1SMartin Kepplinger 	*uHz = mma8452_hp_filter_cutoff[j][i][ret][1];
4781e79841aSMartin Fuzzey 
4791e79841aSMartin Fuzzey 	return 0;
4801e79841aSMartin Fuzzey }
4811e79841aSMartin Fuzzey 
482c7eeea93SPeter Meerwald static int mma8452_read_raw(struct iio_dev *indio_dev,
483c7eeea93SPeter Meerwald 			    struct iio_chan_spec const *chan,
484c7eeea93SPeter Meerwald 			    int *val, int *val2, long mask)
485c7eeea93SPeter Meerwald {
486c7eeea93SPeter Meerwald 	struct mma8452_data *data = iio_priv(indio_dev);
487c7eeea93SPeter Meerwald 	__be16 buffer[3];
488c7eeea93SPeter Meerwald 	int i, ret;
489c7eeea93SPeter Meerwald 
490c7eeea93SPeter Meerwald 	switch (mask) {
491c7eeea93SPeter Meerwald 	case IIO_CHAN_INFO_RAW:
4924d9b0413SAlison Schofield 		ret = iio_device_claim_direct_mode(indio_dev);
4934d9b0413SAlison Schofield 		if (ret)
4944d9b0413SAlison Schofield 			return ret;
495c7eeea93SPeter Meerwald 
496c7eeea93SPeter Meerwald 		mutex_lock(&data->lock);
497c7eeea93SPeter Meerwald 		ret = mma8452_read(data, buffer);
498c7eeea93SPeter Meerwald 		mutex_unlock(&data->lock);
4994d9b0413SAlison Schofield 		iio_device_release_direct_mode(indio_dev);
500c7eeea93SPeter Meerwald 		if (ret < 0)
501c7eeea93SPeter Meerwald 			return ret;
502686027fbSHartmut Knaack 
503c3cdd6e4SMartin Kepplinger 		*val = sign_extend32(be16_to_cpu(
504c3cdd6e4SMartin Kepplinger 			buffer[chan->scan_index]) >> chan->scan_type.shift,
505c3cdd6e4SMartin Kepplinger 			chan->scan_type.realbits - 1);
506686027fbSHartmut Knaack 
507c7eeea93SPeter Meerwald 		return IIO_VAL_INT;
508c7eeea93SPeter Meerwald 	case IIO_CHAN_INFO_SCALE:
509c7eeea93SPeter Meerwald 		i = data->data_cfg & MMA8452_DATA_CFG_FS_MASK;
510c3cdd6e4SMartin Kepplinger 		*val = data->chip_info->mma_scales[i][0];
511c3cdd6e4SMartin Kepplinger 		*val2 = data->chip_info->mma_scales[i][1];
512686027fbSHartmut Knaack 
513c7eeea93SPeter Meerwald 		return IIO_VAL_INT_PLUS_MICRO;
514c7eeea93SPeter Meerwald 	case IIO_CHAN_INFO_SAMP_FREQ:
5155dbbd19fSMartin Fuzzey 		i = mma8452_get_odr_index(data);
516c7eeea93SPeter Meerwald 		*val = mma8452_samp_freq[i][0];
517c7eeea93SPeter Meerwald 		*val2 = mma8452_samp_freq[i][1];
518686027fbSHartmut Knaack 
519c7eeea93SPeter Meerwald 		return IIO_VAL_INT_PLUS_MICRO;
520c7eeea93SPeter Meerwald 	case IIO_CHAN_INFO_CALIBBIAS:
521686027fbSHartmut Knaack 		ret = i2c_smbus_read_byte_data(data->client,
5228b8ff3a6SMartin Kepplinger 					       MMA8452_OFF_X +
5238b8ff3a6SMartin Kepplinger 					       chan->scan_index);
524c7eeea93SPeter Meerwald 		if (ret < 0)
525c7eeea93SPeter Meerwald 			return ret;
526686027fbSHartmut Knaack 
527c7eeea93SPeter Meerwald 		*val = sign_extend32(ret, 7);
528686027fbSHartmut Knaack 
529c7eeea93SPeter Meerwald 		return IIO_VAL_INT;
5301e79841aSMartin Fuzzey 	case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
5311e79841aSMartin Fuzzey 		if (data->data_cfg & MMA8452_DATA_CFG_HPF_MASK) {
5321e79841aSMartin Fuzzey 			ret = mma8452_read_hp_filter(data, val, val2);
5331e79841aSMartin Fuzzey 			if (ret < 0)
5341e79841aSMartin Fuzzey 				return ret;
5351e79841aSMartin Fuzzey 		} else {
5361e79841aSMartin Fuzzey 			*val = 0;
5371e79841aSMartin Fuzzey 			*val2 = 0;
5381e79841aSMartin Fuzzey 		}
539686027fbSHartmut Knaack 
5401e79841aSMartin Fuzzey 		return IIO_VAL_INT_PLUS_MICRO;
541ed859fc1SMartin Kepplinger 	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
542ed859fc1SMartin Kepplinger 		ret = mma8452_get_power_mode(data);
543ed859fc1SMartin Kepplinger 		if (ret < 0)
544ed859fc1SMartin Kepplinger 			return ret;
545ed859fc1SMartin Kepplinger 
546ed859fc1SMartin Kepplinger 		i = mma8452_get_odr_index(data);
547ed859fc1SMartin Kepplinger 
548ed859fc1SMartin Kepplinger 		*val = mma8452_os_ratio[ret][i];
549ed859fc1SMartin Kepplinger 		return IIO_VAL_INT;
550c7eeea93SPeter Meerwald 	}
551686027fbSHartmut Knaack 
552c7eeea93SPeter Meerwald 	return -EINVAL;
553c7eeea93SPeter Meerwald }
554c7eeea93SPeter Meerwald 
555a45d1238SRichard Tresidder static int mma8452_calculate_sleep(struct mma8452_data *data)
556a45d1238SRichard Tresidder {
557a45d1238SRichard Tresidder 	int ret, i = mma8452_get_odr_index(data);
558a45d1238SRichard Tresidder 
559a45d1238SRichard Tresidder 	if (mma8452_samp_freq[i][0] > 0)
560a45d1238SRichard Tresidder 		ret = 1000 / mma8452_samp_freq[i][0];
561a45d1238SRichard Tresidder 	else
562a45d1238SRichard Tresidder 		ret = 1000;
563a45d1238SRichard Tresidder 
564a45d1238SRichard Tresidder 	return ret == 0 ? 1 : ret;
565a45d1238SRichard Tresidder }
566a45d1238SRichard Tresidder 
567c7eeea93SPeter Meerwald static int mma8452_standby(struct mma8452_data *data)
568c7eeea93SPeter Meerwald {
569c7eeea93SPeter Meerwald 	return i2c_smbus_write_byte_data(data->client, MMA8452_CTRL_REG1,
570c7eeea93SPeter Meerwald 					data->ctrl_reg1 & ~MMA8452_CTRL_ACTIVE);
571c7eeea93SPeter Meerwald }
572c7eeea93SPeter Meerwald 
573c7eeea93SPeter Meerwald static int mma8452_active(struct mma8452_data *data)
574c7eeea93SPeter Meerwald {
575c7eeea93SPeter Meerwald 	return i2c_smbus_write_byte_data(data->client, MMA8452_CTRL_REG1,
576c7eeea93SPeter Meerwald 					 data->ctrl_reg1);
577c7eeea93SPeter Meerwald }
578c7eeea93SPeter Meerwald 
579e866853dSMartin Kepplinger /* returns >0 if active, 0 if in standby and <0 on error */
580e866853dSMartin Kepplinger static int mma8452_is_active(struct mma8452_data *data)
581e866853dSMartin Kepplinger {
582e866853dSMartin Kepplinger 	int reg;
583e866853dSMartin Kepplinger 
584e866853dSMartin Kepplinger 	reg = i2c_smbus_read_byte_data(data->client, MMA8452_CTRL_REG1);
585e866853dSMartin Kepplinger 	if (reg < 0)
586e866853dSMartin Kepplinger 		return reg;
587e866853dSMartin Kepplinger 
588e866853dSMartin Kepplinger 	return reg & MMA8452_CTRL_ACTIVE;
589e866853dSMartin Kepplinger }
590e866853dSMartin Kepplinger 
591c7eeea93SPeter Meerwald static int mma8452_change_config(struct mma8452_data *data, u8 reg, u8 val)
592c7eeea93SPeter Meerwald {
593c7eeea93SPeter Meerwald 	int ret;
594e866853dSMartin Kepplinger 	int is_active;
595c7eeea93SPeter Meerwald 
596c7eeea93SPeter Meerwald 	mutex_lock(&data->lock);
597c7eeea93SPeter Meerwald 
598e866853dSMartin Kepplinger 	is_active = mma8452_is_active(data);
599e866853dSMartin Kepplinger 	if (is_active < 0) {
600e866853dSMartin Kepplinger 		ret = is_active;
601e866853dSMartin Kepplinger 		goto fail;
602e866853dSMartin Kepplinger 	}
603e866853dSMartin Kepplinger 
604c7eeea93SPeter Meerwald 	/* config can only be changed when in standby */
605e866853dSMartin Kepplinger 	if (is_active > 0) {
606c7eeea93SPeter Meerwald 		ret = mma8452_standby(data);
607c7eeea93SPeter Meerwald 		if (ret < 0)
608c7eeea93SPeter Meerwald 			goto fail;
609e866853dSMartin Kepplinger 	}
610c7eeea93SPeter Meerwald 
611c7eeea93SPeter Meerwald 	ret = i2c_smbus_write_byte_data(data->client, reg, val);
612c7eeea93SPeter Meerwald 	if (ret < 0)
613c7eeea93SPeter Meerwald 		goto fail;
614c7eeea93SPeter Meerwald 
615e866853dSMartin Kepplinger 	if (is_active > 0) {
616c7eeea93SPeter Meerwald 		ret = mma8452_active(data);
617c7eeea93SPeter Meerwald 		if (ret < 0)
618c7eeea93SPeter Meerwald 			goto fail;
619e866853dSMartin Kepplinger 	}
620c7eeea93SPeter Meerwald 
621c7eeea93SPeter Meerwald 	ret = 0;
622c7eeea93SPeter Meerwald fail:
623c7eeea93SPeter Meerwald 	mutex_unlock(&data->lock);
624686027fbSHartmut Knaack 
625c7eeea93SPeter Meerwald 	return ret;
626c7eeea93SPeter Meerwald }
627c7eeea93SPeter Meerwald 
628ed859fc1SMartin Kepplinger static int mma8452_set_power_mode(struct mma8452_data *data, u8 mode)
629ed859fc1SMartin Kepplinger {
630ed859fc1SMartin Kepplinger 	int reg;
631ed859fc1SMartin Kepplinger 
632ed859fc1SMartin Kepplinger 	reg = i2c_smbus_read_byte_data(data->client,
633ed859fc1SMartin Kepplinger 				       MMA8452_CTRL_REG2);
634ed859fc1SMartin Kepplinger 	if (reg < 0)
635ed859fc1SMartin Kepplinger 		return reg;
636ed859fc1SMartin Kepplinger 
637ed859fc1SMartin Kepplinger 	reg &= ~MMA8452_CTRL_REG2_MODS_MASK;
638ed859fc1SMartin Kepplinger 	reg |= mode << MMA8452_CTRL_REG2_MODS_SHIFT;
639ed859fc1SMartin Kepplinger 
640ed859fc1SMartin Kepplinger 	return mma8452_change_config(data, MMA8452_CTRL_REG2, reg);
641ed859fc1SMartin Kepplinger }
642ed859fc1SMartin Kepplinger 
6438b8ff3a6SMartin Kepplinger /* returns >0 if in freefall mode, 0 if not or <0 if an error occurred */
6444b04266aSMartin Kepplinger static int mma8452_freefall_mode_enabled(struct mma8452_data *data)
6454b04266aSMartin Kepplinger {
6464b04266aSMartin Kepplinger 	int val;
6474b04266aSMartin Kepplinger 
648605f72deSHarinath Nampally 	val = i2c_smbus_read_byte_data(data->client, MMA8452_FF_MT_CFG);
6494b04266aSMartin Kepplinger 	if (val < 0)
6504b04266aSMartin Kepplinger 		return val;
6514b04266aSMartin Kepplinger 
6524b04266aSMartin Kepplinger 	return !(val & MMA8452_FF_MT_CFG_OAE);
6534b04266aSMartin Kepplinger }
6544b04266aSMartin Kepplinger 
6554b04266aSMartin Kepplinger static int mma8452_set_freefall_mode(struct mma8452_data *data, bool state)
6564b04266aSMartin Kepplinger {
6574b04266aSMartin Kepplinger 	int val;
6584b04266aSMartin Kepplinger 
6594b04266aSMartin Kepplinger 	if ((state && mma8452_freefall_mode_enabled(data)) ||
6604b04266aSMartin Kepplinger 	    (!state && !(mma8452_freefall_mode_enabled(data))))
6614b04266aSMartin Kepplinger 		return 0;
6624b04266aSMartin Kepplinger 
663605f72deSHarinath Nampally 	val = i2c_smbus_read_byte_data(data->client, MMA8452_FF_MT_CFG);
6644b04266aSMartin Kepplinger 	if (val < 0)
6654b04266aSMartin Kepplinger 		return val;
6664b04266aSMartin Kepplinger 
6674b04266aSMartin Kepplinger 	if (state) {
668605f72deSHarinath Nampally 		val |= BIT(idx_x + MMA8452_FF_MT_CHAN_SHIFT);
669605f72deSHarinath Nampally 		val |= BIT(idx_y + MMA8452_FF_MT_CHAN_SHIFT);
670605f72deSHarinath Nampally 		val |= BIT(idx_z + MMA8452_FF_MT_CHAN_SHIFT);
6714b04266aSMartin Kepplinger 		val &= ~MMA8452_FF_MT_CFG_OAE;
6724b04266aSMartin Kepplinger 	} else {
673605f72deSHarinath Nampally 		val &= ~BIT(idx_x + MMA8452_FF_MT_CHAN_SHIFT);
674605f72deSHarinath Nampally 		val &= ~BIT(idx_y + MMA8452_FF_MT_CHAN_SHIFT);
675605f72deSHarinath Nampally 		val &= ~BIT(idx_z + MMA8452_FF_MT_CHAN_SHIFT);
6764b04266aSMartin Kepplinger 		val |= MMA8452_FF_MT_CFG_OAE;
6774b04266aSMartin Kepplinger 	}
6784b04266aSMartin Kepplinger 
679605f72deSHarinath Nampally 	return mma8452_change_config(data, MMA8452_FF_MT_CFG, val);
6804b04266aSMartin Kepplinger }
6814b04266aSMartin Kepplinger 
6821e79841aSMartin Fuzzey static int mma8452_set_hp_filter_frequency(struct mma8452_data *data,
6831e79841aSMartin Fuzzey 					   int val, int val2)
6841e79841aSMartin Fuzzey {
6851e79841aSMartin Fuzzey 	int i, reg;
6861e79841aSMartin Fuzzey 
6871e79841aSMartin Fuzzey 	i = mma8452_get_hp_filter_index(data, val, val2);
6881e79841aSMartin Fuzzey 	if (i < 0)
689b9fddcdbSHartmut Knaack 		return i;
6901e79841aSMartin Fuzzey 
6911e79841aSMartin Fuzzey 	reg = i2c_smbus_read_byte_data(data->client,
6921e79841aSMartin Fuzzey 				       MMA8452_HP_FILTER_CUTOFF);
6931e79841aSMartin Fuzzey 	if (reg < 0)
6941e79841aSMartin Fuzzey 		return reg;
695686027fbSHartmut Knaack 
6961e79841aSMartin Fuzzey 	reg &= ~MMA8452_HP_FILTER_CUTOFF_SEL_MASK;
6971e79841aSMartin Fuzzey 	reg |= i;
6981e79841aSMartin Fuzzey 
6991e79841aSMartin Fuzzey 	return mma8452_change_config(data, MMA8452_HP_FILTER_CUTOFF, reg);
7001e79841aSMartin Fuzzey }
7011e79841aSMartin Fuzzey 
702c7eeea93SPeter Meerwald static int mma8452_write_raw(struct iio_dev *indio_dev,
703c7eeea93SPeter Meerwald 			     struct iio_chan_spec const *chan,
704c7eeea93SPeter Meerwald 			     int val, int val2, long mask)
705c7eeea93SPeter Meerwald {
706c7eeea93SPeter Meerwald 	struct mma8452_data *data = iio_priv(indio_dev);
7071e79841aSMartin Fuzzey 	int i, ret;
708c7eeea93SPeter Meerwald 
70979de2ee4SJonathan Cameron 	ret = iio_device_claim_direct_mode(indio_dev);
71079de2ee4SJonathan Cameron 	if (ret)
71179de2ee4SJonathan Cameron 		return ret;
712c7eeea93SPeter Meerwald 
713c7eeea93SPeter Meerwald 	switch (mask) {
714c7eeea93SPeter Meerwald 	case IIO_CHAN_INFO_SAMP_FREQ:
715c7eeea93SPeter Meerwald 		i = mma8452_get_samp_freq_index(data, val, val2);
71679de2ee4SJonathan Cameron 		if (i < 0) {
71779de2ee4SJonathan Cameron 			ret = i;
71879de2ee4SJonathan Cameron 			break;
71979de2ee4SJonathan Cameron 		}
720c7eeea93SPeter Meerwald 		data->ctrl_reg1 &= ~MMA8452_CTRL_DR_MASK;
721c7eeea93SPeter Meerwald 		data->ctrl_reg1 |= i << MMA8452_CTRL_DR_SHIFT;
722686027fbSHartmut Knaack 
723a45d1238SRichard Tresidder 		data->sleep_val = mma8452_calculate_sleep(data);
724a45d1238SRichard Tresidder 
72579de2ee4SJonathan Cameron 		ret = mma8452_change_config(data, MMA8452_CTRL_REG1,
726c7eeea93SPeter Meerwald 					    data->ctrl_reg1);
72779de2ee4SJonathan Cameron 		break;
728c7eeea93SPeter Meerwald 	case IIO_CHAN_INFO_SCALE:
729c7eeea93SPeter Meerwald 		i = mma8452_get_scale_index(data, val, val2);
73079de2ee4SJonathan Cameron 		if (i < 0) {
73179de2ee4SJonathan Cameron 			ret = i;
73279de2ee4SJonathan Cameron 			break;
73379de2ee4SJonathan Cameron 		}
734686027fbSHartmut Knaack 
735c7eeea93SPeter Meerwald 		data->data_cfg &= ~MMA8452_DATA_CFG_FS_MASK;
736c7eeea93SPeter Meerwald 		data->data_cfg |= i;
737686027fbSHartmut Knaack 
73879de2ee4SJonathan Cameron 		ret = mma8452_change_config(data, MMA8452_DATA_CFG,
739c7eeea93SPeter Meerwald 					    data->data_cfg);
74079de2ee4SJonathan Cameron 		break;
741c7eeea93SPeter Meerwald 	case IIO_CHAN_INFO_CALIBBIAS:
74279de2ee4SJonathan Cameron 		if (val < -128 || val > 127) {
74379de2ee4SJonathan Cameron 			ret = -EINVAL;
74479de2ee4SJonathan Cameron 			break;
74579de2ee4SJonathan Cameron 		}
746686027fbSHartmut Knaack 
74779de2ee4SJonathan Cameron 		ret = mma8452_change_config(data,
748686027fbSHartmut Knaack 					    MMA8452_OFF_X + chan->scan_index,
749686027fbSHartmut Knaack 					    val);
75079de2ee4SJonathan Cameron 		break;
7511e79841aSMartin Fuzzey 
7521e79841aSMartin Fuzzey 	case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
7531e79841aSMartin Fuzzey 		if (val == 0 && val2 == 0) {
7541e79841aSMartin Fuzzey 			data->data_cfg &= ~MMA8452_DATA_CFG_HPF_MASK;
7551e79841aSMartin Fuzzey 		} else {
7561e79841aSMartin Fuzzey 			data->data_cfg |= MMA8452_DATA_CFG_HPF_MASK;
7571e79841aSMartin Fuzzey 			ret = mma8452_set_hp_filter_frequency(data, val, val2);
7581e79841aSMartin Fuzzey 			if (ret < 0)
75979de2ee4SJonathan Cameron 				break;
7601e79841aSMartin Fuzzey 		}
761686027fbSHartmut Knaack 
76279de2ee4SJonathan Cameron 		ret = mma8452_change_config(data, MMA8452_DATA_CFG,
7631e79841aSMartin Fuzzey 					     data->data_cfg);
76479de2ee4SJonathan Cameron 		break;
7651e79841aSMartin Fuzzey 
766ed859fc1SMartin Kepplinger 	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
767ed859fc1SMartin Kepplinger 		ret = mma8452_get_odr_index(data);
768ed859fc1SMartin Kepplinger 
769ed859fc1SMartin Kepplinger 		for (i = 0; i < ARRAY_SIZE(mma8452_os_ratio); i++) {
77079de2ee4SJonathan Cameron 			if (mma8452_os_ratio[i][ret] == val) {
77179de2ee4SJonathan Cameron 				ret = mma8452_set_power_mode(data, i);
77279de2ee4SJonathan Cameron 				break;
77379de2ee4SJonathan Cameron 			}
77479de2ee4SJonathan Cameron 		}
77579de2ee4SJonathan Cameron 		break;
77679de2ee4SJonathan Cameron 	default:
77779de2ee4SJonathan Cameron 		ret = -EINVAL;
77879de2ee4SJonathan Cameron 		break;
779ed859fc1SMartin Kepplinger 	}
780ed859fc1SMartin Kepplinger 
78179de2ee4SJonathan Cameron 	iio_device_release_direct_mode(indio_dev);
78279de2ee4SJonathan Cameron 	return ret;
783c7eeea93SPeter Meerwald }
784c7eeea93SPeter Meerwald 
785605f72deSHarinath Nampally static int mma8452_get_event_regs(struct mma8452_data *data,
786605f72deSHarinath Nampally 		const struct iio_chan_spec *chan, enum iio_event_direction dir,
787605f72deSHarinath Nampally 		const struct mma8452_event_regs **ev_reg)
788605f72deSHarinath Nampally {
789605f72deSHarinath Nampally 	if (!chan)
790605f72deSHarinath Nampally 		return -EINVAL;
791605f72deSHarinath Nampally 
792605f72deSHarinath Nampally 	switch (chan->type) {
793605f72deSHarinath Nampally 	case IIO_ACCEL:
794605f72deSHarinath Nampally 		switch (dir) {
795605f72deSHarinath Nampally 		case IIO_EV_DIR_RISING:
796605f72deSHarinath Nampally 			if ((data->chip_info->all_events
797605f72deSHarinath Nampally 					& MMA8452_INT_TRANS) &&
798605f72deSHarinath Nampally 				(data->chip_info->enabled_events
799605f72deSHarinath Nampally 					& MMA8452_INT_TRANS))
800a654c062SHarinath Nampally 				*ev_reg = &trans_ev_regs;
801605f72deSHarinath Nampally 			else
802a654c062SHarinath Nampally 				*ev_reg = &ff_mt_ev_regs;
803605f72deSHarinath Nampally 			return 0;
804605f72deSHarinath Nampally 		case IIO_EV_DIR_FALLING:
805a654c062SHarinath Nampally 			*ev_reg = &ff_mt_ev_regs;
806605f72deSHarinath Nampally 			return 0;
807605f72deSHarinath Nampally 		default:
808605f72deSHarinath Nampally 			return -EINVAL;
809605f72deSHarinath Nampally 		}
810605f72deSHarinath Nampally 	default:
811605f72deSHarinath Nampally 		return -EINVAL;
812605f72deSHarinath Nampally 	}
813605f72deSHarinath Nampally }
814605f72deSHarinath Nampally 
8154febd9f1SHarinath Nampally static int mma8452_read_event_value(struct iio_dev *indio_dev,
81628e34278SMartin Fuzzey 			       const struct iio_chan_spec *chan,
81728e34278SMartin Fuzzey 			       enum iio_event_type type,
81828e34278SMartin Fuzzey 			       enum iio_event_direction dir,
81928e34278SMartin Fuzzey 			       enum iio_event_info info,
82028e34278SMartin Fuzzey 			       int *val, int *val2)
82128e34278SMartin Fuzzey {
82228e34278SMartin Fuzzey 	struct mma8452_data *data = iio_priv(indio_dev);
823ed859fc1SMartin Kepplinger 	int ret, us, power_mode;
824605f72deSHarinath Nampally 	const struct mma8452_event_regs *ev_regs;
825605f72deSHarinath Nampally 
826605f72deSHarinath Nampally 	ret = mma8452_get_event_regs(data, chan, dir, &ev_regs);
827605f72deSHarinath Nampally 	if (ret)
828605f72deSHarinath Nampally 		return ret;
82928e34278SMartin Fuzzey 
8305dbbd19fSMartin Fuzzey 	switch (info) {
8315dbbd19fSMartin Fuzzey 	case IIO_EV_INFO_VALUE:
832605f72deSHarinath Nampally 		ret = i2c_smbus_read_byte_data(data->client, ev_regs->ev_ths);
83328e34278SMartin Fuzzey 		if (ret < 0)
83428e34278SMartin Fuzzey 			return ret;
83528e34278SMartin Fuzzey 
836605f72deSHarinath Nampally 		*val = ret & ev_regs->ev_ths_mask;
837686027fbSHartmut Knaack 
83828e34278SMartin Fuzzey 		return IIO_VAL_INT;
8395dbbd19fSMartin Fuzzey 
8405dbbd19fSMartin Fuzzey 	case IIO_EV_INFO_PERIOD:
841605f72deSHarinath Nampally 		ret = i2c_smbus_read_byte_data(data->client, ev_regs->ev_count);
8425dbbd19fSMartin Fuzzey 		if (ret < 0)
8435dbbd19fSMartin Fuzzey 			return ret;
8445dbbd19fSMartin Fuzzey 
845ed859fc1SMartin Kepplinger 		power_mode = mma8452_get_power_mode(data);
846ed859fc1SMartin Kepplinger 		if (power_mode < 0)
847ed859fc1SMartin Kepplinger 			return power_mode;
848ed859fc1SMartin Kepplinger 
849cc54a660SHarinath Nampally 		us = ret * mma8452_time_step_us[power_mode][
8505dbbd19fSMartin Fuzzey 				mma8452_get_odr_index(data)];
8515dbbd19fSMartin Fuzzey 		*val = us / USEC_PER_SEC;
8525dbbd19fSMartin Fuzzey 		*val2 = us % USEC_PER_SEC;
853686027fbSHartmut Knaack 
8545dbbd19fSMartin Fuzzey 		return IIO_VAL_INT_PLUS_MICRO;
8555dbbd19fSMartin Fuzzey 
8561e79841aSMartin Fuzzey 	case IIO_EV_INFO_HIGH_PASS_FILTER_3DB:
8571e79841aSMartin Fuzzey 		ret = i2c_smbus_read_byte_data(data->client,
8581e79841aSMartin Fuzzey 					       MMA8452_TRANSIENT_CFG);
8591e79841aSMartin Fuzzey 		if (ret < 0)
8601e79841aSMartin Fuzzey 			return ret;
8611e79841aSMartin Fuzzey 
8621e79841aSMartin Fuzzey 		if (ret & MMA8452_TRANSIENT_CFG_HPF_BYP) {
8631e79841aSMartin Fuzzey 			*val = 0;
8641e79841aSMartin Fuzzey 			*val2 = 0;
8651e79841aSMartin Fuzzey 		} else {
8661e79841aSMartin Fuzzey 			ret = mma8452_read_hp_filter(data, val, val2);
8671e79841aSMartin Fuzzey 			if (ret < 0)
8681e79841aSMartin Fuzzey 				return ret;
8691e79841aSMartin Fuzzey 		}
870686027fbSHartmut Knaack 
8711e79841aSMartin Fuzzey 		return IIO_VAL_INT_PLUS_MICRO;
8721e79841aSMartin Fuzzey 
8735dbbd19fSMartin Fuzzey 	default:
8745dbbd19fSMartin Fuzzey 		return -EINVAL;
8755dbbd19fSMartin Fuzzey 	}
87628e34278SMartin Fuzzey }
87728e34278SMartin Fuzzey 
8784febd9f1SHarinath Nampally static int mma8452_write_event_value(struct iio_dev *indio_dev,
87928e34278SMartin Fuzzey 				const struct iio_chan_spec *chan,
88028e34278SMartin Fuzzey 				enum iio_event_type type,
88128e34278SMartin Fuzzey 				enum iio_event_direction dir,
88228e34278SMartin Fuzzey 				enum iio_event_info info,
88328e34278SMartin Fuzzey 				int val, int val2)
88428e34278SMartin Fuzzey {
88528e34278SMartin Fuzzey 	struct mma8452_data *data = iio_priv(indio_dev);
8861e79841aSMartin Fuzzey 	int ret, reg, steps;
887605f72deSHarinath Nampally 	const struct mma8452_event_regs *ev_regs;
888605f72deSHarinath Nampally 
889605f72deSHarinath Nampally 	ret = mma8452_get_event_regs(data, chan, dir, &ev_regs);
890605f72deSHarinath Nampally 	if (ret)
891605f72deSHarinath Nampally 		return ret;
89228e34278SMartin Fuzzey 
8935dbbd19fSMartin Fuzzey 	switch (info) {
8945dbbd19fSMartin Fuzzey 	case IIO_EV_INFO_VALUE:
895605f72deSHarinath Nampally 		if (val < 0 || val > ev_regs->ev_ths_mask)
89611218226SHartmut Knaack 			return -EINVAL;
89711218226SHartmut Knaack 
898605f72deSHarinath Nampally 		return mma8452_change_config(data, ev_regs->ev_ths, val);
8995dbbd19fSMartin Fuzzey 
9005dbbd19fSMartin Fuzzey 	case IIO_EV_INFO_PERIOD:
901ed859fc1SMartin Kepplinger 		ret = mma8452_get_power_mode(data);
902ed859fc1SMartin Kepplinger 		if (ret < 0)
903ed859fc1SMartin Kepplinger 			return ret;
904ed859fc1SMartin Kepplinger 
9055dbbd19fSMartin Fuzzey 		steps = (val * USEC_PER_SEC + val2) /
906cc54a660SHarinath Nampally 				mma8452_time_step_us[ret][
9075dbbd19fSMartin Fuzzey 					mma8452_get_odr_index(data)];
9085dbbd19fSMartin Fuzzey 
90911218226SHartmut Knaack 		if (steps < 0 || steps > 0xff)
9105dbbd19fSMartin Fuzzey 			return -EINVAL;
9115dbbd19fSMartin Fuzzey 
912605f72deSHarinath Nampally 		return mma8452_change_config(data, ev_regs->ev_count, steps);
913686027fbSHartmut Knaack 
9141e79841aSMartin Fuzzey 	case IIO_EV_INFO_HIGH_PASS_FILTER_3DB:
9151e79841aSMartin Fuzzey 		reg = i2c_smbus_read_byte_data(data->client,
9161e79841aSMartin Fuzzey 					       MMA8452_TRANSIENT_CFG);
9171e79841aSMartin Fuzzey 		if (reg < 0)
9181e79841aSMartin Fuzzey 			return reg;
9191e79841aSMartin Fuzzey 
9201e79841aSMartin Fuzzey 		if (val == 0 && val2 == 0) {
9211e79841aSMartin Fuzzey 			reg |= MMA8452_TRANSIENT_CFG_HPF_BYP;
9221e79841aSMartin Fuzzey 		} else {
9231e79841aSMartin Fuzzey 			reg &= ~MMA8452_TRANSIENT_CFG_HPF_BYP;
9241e79841aSMartin Fuzzey 			ret = mma8452_set_hp_filter_frequency(data, val, val2);
9251e79841aSMartin Fuzzey 			if (ret < 0)
9261e79841aSMartin Fuzzey 				return ret;
9271e79841aSMartin Fuzzey 		}
928686027fbSHartmut Knaack 
9291e79841aSMartin Fuzzey 		return mma8452_change_config(data, MMA8452_TRANSIENT_CFG, reg);
9301e79841aSMartin Fuzzey 
9315dbbd19fSMartin Fuzzey 	default:
9325dbbd19fSMartin Fuzzey 		return -EINVAL;
9335dbbd19fSMartin Fuzzey 	}
93428e34278SMartin Fuzzey }
93528e34278SMartin Fuzzey 
93628e34278SMartin Fuzzey static int mma8452_read_event_config(struct iio_dev *indio_dev,
93728e34278SMartin Fuzzey 				     const struct iio_chan_spec *chan,
93828e34278SMartin Fuzzey 				     enum iio_event_type type,
93928e34278SMartin Fuzzey 				     enum iio_event_direction dir)
94028e34278SMartin Fuzzey {
94128e34278SMartin Fuzzey 	struct mma8452_data *data = iio_priv(indio_dev);
94228e34278SMartin Fuzzey 	int ret;
943605f72deSHarinath Nampally 	const struct mma8452_event_regs *ev_regs;
944605f72deSHarinath Nampally 
945605f72deSHarinath Nampally 	ret = mma8452_get_event_regs(data, chan, dir, &ev_regs);
946605f72deSHarinath Nampally 	if (ret)
947605f72deSHarinath Nampally 		return ret;
94828e34278SMartin Fuzzey 
9494b04266aSMartin Kepplinger 	switch (dir) {
9504b04266aSMartin Kepplinger 	case IIO_EV_DIR_FALLING:
9514b04266aSMartin Kepplinger 		return mma8452_freefall_mode_enabled(data);
9524b04266aSMartin Kepplinger 	case IIO_EV_DIR_RISING:
953c3cdd6e4SMartin Kepplinger 		ret = i2c_smbus_read_byte_data(data->client,
954605f72deSHarinath Nampally 				ev_regs->ev_cfg);
95528e34278SMartin Fuzzey 		if (ret < 0)
95628e34278SMartin Fuzzey 			return ret;
95728e34278SMartin Fuzzey 
9588b8ff3a6SMartin Kepplinger 		return !!(ret & BIT(chan->scan_index +
959605f72deSHarinath Nampally 				ev_regs->ev_cfg_chan_shift));
9604b04266aSMartin Kepplinger 	default:
9614b04266aSMartin Kepplinger 		return -EINVAL;
9624b04266aSMartin Kepplinger 	}
96328e34278SMartin Fuzzey }
96428e34278SMartin Fuzzey 
96528e34278SMartin Fuzzey static int mma8452_write_event_config(struct iio_dev *indio_dev,
96628e34278SMartin Fuzzey 				      const struct iio_chan_spec *chan,
96728e34278SMartin Fuzzey 				      enum iio_event_type type,
96828e34278SMartin Fuzzey 				      enum iio_event_direction dir,
96928e34278SMartin Fuzzey 				      int state)
97028e34278SMartin Fuzzey {
97128e34278SMartin Fuzzey 	struct mma8452_data *data = iio_priv(indio_dev);
97296c0cb2bSMartin Kepplinger 	int val, ret;
973605f72deSHarinath Nampally 	const struct mma8452_event_regs *ev_regs;
974605f72deSHarinath Nampally 
975605f72deSHarinath Nampally 	ret = mma8452_get_event_regs(data, chan, dir, &ev_regs);
976605f72deSHarinath Nampally 	if (ret)
977605f72deSHarinath Nampally 		return ret;
97896c0cb2bSMartin Kepplinger 
97996c0cb2bSMartin Kepplinger 	ret = mma8452_set_runtime_pm_state(data->client, state);
98096c0cb2bSMartin Kepplinger 	if (ret)
98196c0cb2bSMartin Kepplinger 		return ret;
98228e34278SMartin Fuzzey 
9834b04266aSMartin Kepplinger 	switch (dir) {
9844b04266aSMartin Kepplinger 	case IIO_EV_DIR_FALLING:
9854b04266aSMartin Kepplinger 		return mma8452_set_freefall_mode(data, state);
9864b04266aSMartin Kepplinger 	case IIO_EV_DIR_RISING:
987605f72deSHarinath Nampally 		val = i2c_smbus_read_byte_data(data->client, ev_regs->ev_cfg);
98828e34278SMartin Fuzzey 		if (val < 0)
98928e34278SMartin Fuzzey 			return val;
99028e34278SMartin Fuzzey 
9914b04266aSMartin Kepplinger 		if (state) {
9924b04266aSMartin Kepplinger 			if (mma8452_freefall_mode_enabled(data)) {
993605f72deSHarinath Nampally 				val &= ~BIT(idx_x + ev_regs->ev_cfg_chan_shift);
994605f72deSHarinath Nampally 				val &= ~BIT(idx_y + ev_regs->ev_cfg_chan_shift);
995605f72deSHarinath Nampally 				val &= ~BIT(idx_z + ev_regs->ev_cfg_chan_shift);
9964b04266aSMartin Kepplinger 				val |= MMA8452_FF_MT_CFG_OAE;
9974b04266aSMartin Kepplinger 			}
998605f72deSHarinath Nampally 			val |= BIT(chan->scan_index +
999605f72deSHarinath Nampally 					ev_regs->ev_cfg_chan_shift);
10004b04266aSMartin Kepplinger 		} else {
10014b04266aSMartin Kepplinger 			if (mma8452_freefall_mode_enabled(data))
10024b04266aSMartin Kepplinger 				return 0;
10034b04266aSMartin Kepplinger 
1004605f72deSHarinath Nampally 			val &= ~BIT(chan->scan_index +
1005605f72deSHarinath Nampally 					ev_regs->ev_cfg_chan_shift);
10064b04266aSMartin Kepplinger 		}
100728e34278SMartin Fuzzey 
1008605f72deSHarinath Nampally 		val |= ev_regs->ev_cfg_ele;
100928e34278SMartin Fuzzey 
1010605f72deSHarinath Nampally 		return mma8452_change_config(data, ev_regs->ev_cfg, val);
10114b04266aSMartin Kepplinger 	default:
10124b04266aSMartin Kepplinger 		return -EINVAL;
10134b04266aSMartin Kepplinger 	}
101428e34278SMartin Fuzzey }
101528e34278SMartin Fuzzey 
101628e34278SMartin Fuzzey static void mma8452_transient_interrupt(struct iio_dev *indio_dev)
101728e34278SMartin Fuzzey {
101828e34278SMartin Fuzzey 	struct mma8452_data *data = iio_priv(indio_dev);
1019bc2b7dabSGregor Boirie 	s64 ts = iio_get_time_ns(indio_dev);
102028e34278SMartin Fuzzey 	int src;
102128e34278SMartin Fuzzey 
1022605f72deSHarinath Nampally 	src = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_SRC);
102328e34278SMartin Fuzzey 	if (src < 0)
102428e34278SMartin Fuzzey 		return;
102528e34278SMartin Fuzzey 
1026605f72deSHarinath Nampally 	if (src & MMA8452_TRANSIENT_SRC_XTRANSE)
102728e34278SMartin Fuzzey 		iio_push_event(indio_dev,
102828e34278SMartin Fuzzey 			       IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X,
1029c5d0db06SMartin Kepplinger 						  IIO_EV_TYPE_MAG,
103028e34278SMartin Fuzzey 						  IIO_EV_DIR_RISING),
103128e34278SMartin Fuzzey 			       ts);
103228e34278SMartin Fuzzey 
1033605f72deSHarinath Nampally 	if (src & MMA8452_TRANSIENT_SRC_YTRANSE)
103428e34278SMartin Fuzzey 		iio_push_event(indio_dev,
103528e34278SMartin Fuzzey 			       IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Y,
1036c5d0db06SMartin Kepplinger 						  IIO_EV_TYPE_MAG,
103728e34278SMartin Fuzzey 						  IIO_EV_DIR_RISING),
103828e34278SMartin Fuzzey 			       ts);
103928e34278SMartin Fuzzey 
1040605f72deSHarinath Nampally 	if (src & MMA8452_TRANSIENT_SRC_ZTRANSE)
104128e34278SMartin Fuzzey 		iio_push_event(indio_dev,
104228e34278SMartin Fuzzey 			       IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Z,
1043c5d0db06SMartin Kepplinger 						  IIO_EV_TYPE_MAG,
104428e34278SMartin Fuzzey 						  IIO_EV_DIR_RISING),
104528e34278SMartin Fuzzey 			       ts);
104628e34278SMartin Fuzzey }
104728e34278SMartin Fuzzey 
104828e34278SMartin Fuzzey static irqreturn_t mma8452_interrupt(int irq, void *p)
104928e34278SMartin Fuzzey {
105028e34278SMartin Fuzzey 	struct iio_dev *indio_dev = p;
105128e34278SMartin Fuzzey 	struct mma8452_data *data = iio_priv(indio_dev);
1052ae6d9ce0SMartin Fuzzey 	int ret = IRQ_NONE;
105328e34278SMartin Fuzzey 	int src;
105428e34278SMartin Fuzzey 
105528e34278SMartin Fuzzey 	src = i2c_smbus_read_byte_data(data->client, MMA8452_INT_SRC);
105628e34278SMartin Fuzzey 	if (src < 0)
105728e34278SMartin Fuzzey 		return IRQ_NONE;
105828e34278SMartin Fuzzey 
1059b02ec67aSLeonard Crestez 	if (!(src & (data->chip_info->enabled_events | MMA8452_INT_DRDY)))
1060605f72deSHarinath Nampally 		return IRQ_NONE;
1061605f72deSHarinath Nampally 
1062ae6d9ce0SMartin Fuzzey 	if (src & MMA8452_INT_DRDY) {
1063ae6d9ce0SMartin Fuzzey 		iio_trigger_poll_chained(indio_dev->trig);
1064ae6d9ce0SMartin Fuzzey 		ret = IRQ_HANDLED;
106528e34278SMartin Fuzzey 	}
106628e34278SMartin Fuzzey 
1067605f72deSHarinath Nampally 	if (src & MMA8452_INT_FF_MT) {
1068605f72deSHarinath Nampally 		if (mma8452_freefall_mode_enabled(data)) {
1069605f72deSHarinath Nampally 			s64 ts = iio_get_time_ns(indio_dev);
1070605f72deSHarinath Nampally 
1071605f72deSHarinath Nampally 			iio_push_event(indio_dev,
1072605f72deSHarinath Nampally 				       IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
1073605f72deSHarinath Nampally 							  IIO_MOD_X_AND_Y_AND_Z,
1074605f72deSHarinath Nampally 							  IIO_EV_TYPE_MAG,
1075605f72deSHarinath Nampally 							  IIO_EV_DIR_FALLING),
1076605f72deSHarinath Nampally 					ts);
1077605f72deSHarinath Nampally 		}
1078605f72deSHarinath Nampally 		ret = IRQ_HANDLED;
1079605f72deSHarinath Nampally 	}
1080605f72deSHarinath Nampally 
1081605f72deSHarinath Nampally 	if (src & MMA8452_INT_TRANS) {
1082ae6d9ce0SMartin Fuzzey 		mma8452_transient_interrupt(indio_dev);
1083ae6d9ce0SMartin Fuzzey 		ret = IRQ_HANDLED;
1084ae6d9ce0SMartin Fuzzey 	}
1085ae6d9ce0SMartin Fuzzey 
1086ae6d9ce0SMartin Fuzzey 	return ret;
108728e34278SMartin Fuzzey }
108828e34278SMartin Fuzzey 
1089c7eeea93SPeter Meerwald static irqreturn_t mma8452_trigger_handler(int irq, void *p)
1090c7eeea93SPeter Meerwald {
1091c7eeea93SPeter Meerwald 	struct iio_poll_func *pf = p;
1092c7eeea93SPeter Meerwald 	struct iio_dev *indio_dev = pf->indio_dev;
1093c7eeea93SPeter Meerwald 	struct mma8452_data *data = iio_priv(indio_dev);
1094c7eeea93SPeter Meerwald 	u8 buffer[16]; /* 3 16-bit channels + padding + ts */
1095c7eeea93SPeter Meerwald 	int ret;
1096c7eeea93SPeter Meerwald 
1097c7eeea93SPeter Meerwald 	ret = mma8452_read(data, (__be16 *)buffer);
1098c7eeea93SPeter Meerwald 	if (ret < 0)
1099c7eeea93SPeter Meerwald 		goto done;
1100c7eeea93SPeter Meerwald 
1101c7eeea93SPeter Meerwald 	iio_push_to_buffers_with_timestamp(indio_dev, buffer,
1102bc2b7dabSGregor Boirie 					   iio_get_time_ns(indio_dev));
1103c7eeea93SPeter Meerwald 
1104c7eeea93SPeter Meerwald done:
1105c7eeea93SPeter Meerwald 	iio_trigger_notify_done(indio_dev->trig);
1106686027fbSHartmut Knaack 
1107c7eeea93SPeter Meerwald 	return IRQ_HANDLED;
1108c7eeea93SPeter Meerwald }
1109c7eeea93SPeter Meerwald 
11102a17698cSMartin Fuzzey static int mma8452_reg_access_dbg(struct iio_dev *indio_dev,
1111f8b7b30fSHarinath Nampally 				  unsigned int reg, unsigned int writeval,
1112f8b7b30fSHarinath Nampally 				  unsigned int *readval)
11132a17698cSMartin Fuzzey {
11142a17698cSMartin Fuzzey 	int ret;
11152a17698cSMartin Fuzzey 	struct mma8452_data *data = iio_priv(indio_dev);
11162a17698cSMartin Fuzzey 
11172a17698cSMartin Fuzzey 	if (reg > MMA8452_MAX_REG)
11182a17698cSMartin Fuzzey 		return -EINVAL;
11192a17698cSMartin Fuzzey 
11202a17698cSMartin Fuzzey 	if (!readval)
11212a17698cSMartin Fuzzey 		return mma8452_change_config(data, reg, writeval);
11222a17698cSMartin Fuzzey 
11232a17698cSMartin Fuzzey 	ret = i2c_smbus_read_byte_data(data->client, reg);
11242a17698cSMartin Fuzzey 	if (ret < 0)
11252a17698cSMartin Fuzzey 		return ret;
11262a17698cSMartin Fuzzey 
11272a17698cSMartin Fuzzey 	*readval = ret;
11282a17698cSMartin Fuzzey 
11292a17698cSMartin Fuzzey 	return 0;
11302a17698cSMartin Fuzzey }
11312a17698cSMartin Fuzzey 
11324b04266aSMartin Kepplinger static const struct iio_event_spec mma8452_freefall_event[] = {
11334b04266aSMartin Kepplinger 	{
11344b04266aSMartin Kepplinger 		.type = IIO_EV_TYPE_MAG,
11354b04266aSMartin Kepplinger 		.dir = IIO_EV_DIR_FALLING,
11364b04266aSMartin Kepplinger 		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
11374b04266aSMartin Kepplinger 		.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
11384b04266aSMartin Kepplinger 					BIT(IIO_EV_INFO_PERIOD) |
11394b04266aSMartin Kepplinger 					BIT(IIO_EV_INFO_HIGH_PASS_FILTER_3DB)
11404b04266aSMartin Kepplinger 	},
11414b04266aSMartin Kepplinger };
11424b04266aSMartin Kepplinger 
11434b04266aSMartin Kepplinger static const struct iio_event_spec mma8652_freefall_event[] = {
11444b04266aSMartin Kepplinger 	{
11454b04266aSMartin Kepplinger 		.type = IIO_EV_TYPE_MAG,
11464b04266aSMartin Kepplinger 		.dir = IIO_EV_DIR_FALLING,
11474b04266aSMartin Kepplinger 		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
11484b04266aSMartin Kepplinger 		.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
11494b04266aSMartin Kepplinger 					BIT(IIO_EV_INFO_PERIOD)
11504b04266aSMartin Kepplinger 	},
11514b04266aSMartin Kepplinger };
11524b04266aSMartin Kepplinger 
115328e34278SMartin Fuzzey static const struct iio_event_spec mma8452_transient_event[] = {
115428e34278SMartin Fuzzey 	{
1155c5d0db06SMartin Kepplinger 		.type = IIO_EV_TYPE_MAG,
115628e34278SMartin Fuzzey 		.dir = IIO_EV_DIR_RISING,
115728e34278SMartin Fuzzey 		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
11585dbbd19fSMartin Fuzzey 		.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
11591e79841aSMartin Fuzzey 					BIT(IIO_EV_INFO_PERIOD) |
11601e79841aSMartin Fuzzey 					BIT(IIO_EV_INFO_HIGH_PASS_FILTER_3DB)
116128e34278SMartin Fuzzey 	},
116228e34278SMartin Fuzzey };
116328e34278SMartin Fuzzey 
116460f562e7SMartin Kepplinger static const struct iio_event_spec mma8452_motion_event[] = {
116560f562e7SMartin Kepplinger 	{
116660f562e7SMartin Kepplinger 		.type = IIO_EV_TYPE_MAG,
116760f562e7SMartin Kepplinger 		.dir = IIO_EV_DIR_RISING,
116860f562e7SMartin Kepplinger 		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
116960f562e7SMartin Kepplinger 		.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
117060f562e7SMartin Kepplinger 					BIT(IIO_EV_INFO_PERIOD)
117160f562e7SMartin Kepplinger 	},
117260f562e7SMartin Kepplinger };
117360f562e7SMartin Kepplinger 
117428e34278SMartin Fuzzey /*
117528e34278SMartin Fuzzey  * Threshold is configured in fixed 8G/127 steps regardless of
117628e34278SMartin Fuzzey  * currently selected scale for measurement.
117728e34278SMartin Fuzzey  */
117828e34278SMartin Fuzzey static IIO_CONST_ATTR_NAMED(accel_transient_scale, in_accel_scale, "0.617742");
117928e34278SMartin Fuzzey 
118028e34278SMartin Fuzzey static struct attribute *mma8452_event_attributes[] = {
118128e34278SMartin Fuzzey 	&iio_const_attr_accel_transient_scale.dev_attr.attr,
118228e34278SMartin Fuzzey 	NULL,
118328e34278SMartin Fuzzey };
118428e34278SMartin Fuzzey 
118528e34278SMartin Fuzzey static struct attribute_group mma8452_event_attribute_group = {
118628e34278SMartin Fuzzey 	.attrs = mma8452_event_attributes,
118728e34278SMartin Fuzzey };
118828e34278SMartin Fuzzey 
11894b04266aSMartin Kepplinger #define MMA8452_FREEFALL_CHANNEL(modifier) { \
11904b04266aSMartin Kepplinger 	.type = IIO_ACCEL, \
11914b04266aSMartin Kepplinger 	.modified = 1, \
11924b04266aSMartin Kepplinger 	.channel2 = modifier, \
11934b04266aSMartin Kepplinger 	.scan_index = -1, \
11944b04266aSMartin Kepplinger 	.event_spec = mma8452_freefall_event, \
11954b04266aSMartin Kepplinger 	.num_event_specs = ARRAY_SIZE(mma8452_freefall_event), \
11964b04266aSMartin Kepplinger }
11974b04266aSMartin Kepplinger 
11984b04266aSMartin Kepplinger #define MMA8652_FREEFALL_CHANNEL(modifier) { \
11994b04266aSMartin Kepplinger 	.type = IIO_ACCEL, \
12004b04266aSMartin Kepplinger 	.modified = 1, \
12014b04266aSMartin Kepplinger 	.channel2 = modifier, \
12024b04266aSMartin Kepplinger 	.scan_index = -1, \
12034b04266aSMartin Kepplinger 	.event_spec = mma8652_freefall_event, \
12044b04266aSMartin Kepplinger 	.num_event_specs = ARRAY_SIZE(mma8652_freefall_event), \
12054b04266aSMartin Kepplinger }
12064b04266aSMartin Kepplinger 
1207c3cdd6e4SMartin Kepplinger #define MMA8452_CHANNEL(axis, idx, bits) { \
1208c7eeea93SPeter Meerwald 	.type = IIO_ACCEL, \
1209c7eeea93SPeter Meerwald 	.modified = 1, \
1210c7eeea93SPeter Meerwald 	.channel2 = IIO_MOD_##axis, \
1211c7eeea93SPeter Meerwald 	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
1212c7eeea93SPeter Meerwald 			      BIT(IIO_CHAN_INFO_CALIBBIAS), \
1213c7eeea93SPeter Meerwald 	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
12141e79841aSMartin Fuzzey 			BIT(IIO_CHAN_INFO_SCALE) | \
1215ed859fc1SMartin Kepplinger 			BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY) | \
1216ed859fc1SMartin Kepplinger 			BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
1217c7eeea93SPeter Meerwald 	.scan_index = idx, \
1218c7eeea93SPeter Meerwald 	.scan_type = { \
1219c7eeea93SPeter Meerwald 		.sign = 's', \
1220c3cdd6e4SMartin Kepplinger 		.realbits = (bits), \
1221c7eeea93SPeter Meerwald 		.storagebits = 16, \
1222c3cdd6e4SMartin Kepplinger 		.shift = 16 - (bits), \
1223c7eeea93SPeter Meerwald 		.endianness = IIO_BE, \
1224c7eeea93SPeter Meerwald 	}, \
122528e34278SMartin Fuzzey 	.event_spec = mma8452_transient_event, \
122628e34278SMartin Fuzzey 	.num_event_specs = ARRAY_SIZE(mma8452_transient_event), \
1227c7eeea93SPeter Meerwald }
1228c7eeea93SPeter Meerwald 
1229417e008bSMartin Kepplinger #define MMA8652_CHANNEL(axis, idx, bits) { \
1230417e008bSMartin Kepplinger 	.type = IIO_ACCEL, \
1231417e008bSMartin Kepplinger 	.modified = 1, \
1232417e008bSMartin Kepplinger 	.channel2 = IIO_MOD_##axis, \
1233417e008bSMartin Kepplinger 	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
1234417e008bSMartin Kepplinger 		BIT(IIO_CHAN_INFO_CALIBBIAS), \
1235417e008bSMartin Kepplinger 	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
1236ed859fc1SMartin Kepplinger 		BIT(IIO_CHAN_INFO_SCALE) | \
1237ed859fc1SMartin Kepplinger 		BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
1238417e008bSMartin Kepplinger 	.scan_index = idx, \
1239417e008bSMartin Kepplinger 	.scan_type = { \
1240417e008bSMartin Kepplinger 		.sign = 's', \
1241417e008bSMartin Kepplinger 		.realbits = (bits), \
1242417e008bSMartin Kepplinger 		.storagebits = 16, \
1243417e008bSMartin Kepplinger 		.shift = 16 - (bits), \
1244417e008bSMartin Kepplinger 		.endianness = IIO_BE, \
1245417e008bSMartin Kepplinger 	}, \
1246417e008bSMartin Kepplinger 	.event_spec = mma8452_motion_event, \
1247417e008bSMartin Kepplinger 	.num_event_specs = ARRAY_SIZE(mma8452_motion_event), \
1248417e008bSMartin Kepplinger }
1249417e008bSMartin Kepplinger 
1250244a93f6SMartin Kepplinger static const struct iio_chan_spec mma8451_channels[] = {
1251244a93f6SMartin Kepplinger 	MMA8452_CHANNEL(X, idx_x, 14),
1252244a93f6SMartin Kepplinger 	MMA8452_CHANNEL(Y, idx_y, 14),
1253244a93f6SMartin Kepplinger 	MMA8452_CHANNEL(Z, idx_z, 14),
1254244a93f6SMartin Kepplinger 	IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
1255244a93f6SMartin Kepplinger 	MMA8452_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
1256244a93f6SMartin Kepplinger };
1257244a93f6SMartin Kepplinger 
1258c7eeea93SPeter Meerwald static const struct iio_chan_spec mma8452_channels[] = {
1259e60378c1SMartin Kepplinger 	MMA8452_CHANNEL(X, idx_x, 12),
1260e60378c1SMartin Kepplinger 	MMA8452_CHANNEL(Y, idx_y, 12),
1261e60378c1SMartin Kepplinger 	MMA8452_CHANNEL(Z, idx_z, 12),
1262e60378c1SMartin Kepplinger 	IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
12634b04266aSMartin Kepplinger 	MMA8452_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
1264c7eeea93SPeter Meerwald };
1265c7eeea93SPeter Meerwald 
1266c5ea1b58SMartin Kepplinger static const struct iio_chan_spec mma8453_channels[] = {
1267e60378c1SMartin Kepplinger 	MMA8452_CHANNEL(X, idx_x, 10),
1268e60378c1SMartin Kepplinger 	MMA8452_CHANNEL(Y, idx_y, 10),
1269e60378c1SMartin Kepplinger 	MMA8452_CHANNEL(Z, idx_z, 10),
1270e60378c1SMartin Kepplinger 	IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
12714b04266aSMartin Kepplinger 	MMA8452_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
1272c5ea1b58SMartin Kepplinger };
1273c5ea1b58SMartin Kepplinger 
1274417e008bSMartin Kepplinger static const struct iio_chan_spec mma8652_channels[] = {
1275e60378c1SMartin Kepplinger 	MMA8652_CHANNEL(X, idx_x, 12),
1276e60378c1SMartin Kepplinger 	MMA8652_CHANNEL(Y, idx_y, 12),
1277e60378c1SMartin Kepplinger 	MMA8652_CHANNEL(Z, idx_z, 12),
1278e60378c1SMartin Kepplinger 	IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
12794b04266aSMartin Kepplinger 	MMA8652_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
1280417e008bSMartin Kepplinger };
1281417e008bSMartin Kepplinger 
1282417e008bSMartin Kepplinger static const struct iio_chan_spec mma8653_channels[] = {
1283e60378c1SMartin Kepplinger 	MMA8652_CHANNEL(X, idx_x, 10),
1284e60378c1SMartin Kepplinger 	MMA8652_CHANNEL(Y, idx_y, 10),
1285e60378c1SMartin Kepplinger 	MMA8652_CHANNEL(Z, idx_z, 10),
1286e60378c1SMartin Kepplinger 	IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
12874b04266aSMartin Kepplinger 	MMA8652_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
1288417e008bSMartin Kepplinger };
1289417e008bSMartin Kepplinger 
1290c3cdd6e4SMartin Kepplinger enum {
1291244a93f6SMartin Kepplinger 	mma8451,
1292c3cdd6e4SMartin Kepplinger 	mma8452,
1293c5ea1b58SMartin Kepplinger 	mma8453,
1294417e008bSMartin Kepplinger 	mma8652,
1295417e008bSMartin Kepplinger 	mma8653,
1296e8731180SMartin Kepplinger 	fxls8471,
1297c3cdd6e4SMartin Kepplinger };
1298c3cdd6e4SMartin Kepplinger 
1299c3cdd6e4SMartin Kepplinger static const struct mma_chip_info mma_chip_info_table[] = {
1300244a93f6SMartin Kepplinger 	[mma8451] = {
1301244a93f6SMartin Kepplinger 		.chip_id = MMA8451_DEVICE_ID,
1302244a93f6SMartin Kepplinger 		.channels = mma8451_channels,
1303244a93f6SMartin Kepplinger 		.num_channels = ARRAY_SIZE(mma8451_channels),
1304c3cdd6e4SMartin Kepplinger 		/*
1305c3cdd6e4SMartin Kepplinger 		 * Hardware has fullscale of -2G, -4G, -8G corresponding to
1306244a93f6SMartin Kepplinger 		 * raw value -8192 for 14 bit, -2048 for 12 bit or -512 for 10
1307244a93f6SMartin Kepplinger 		 * bit.
1308c3cdd6e4SMartin Kepplinger 		 * The userspace interface uses m/s^2 and we declare micro units
1309c3cdd6e4SMartin Kepplinger 		 * So scale factor for 12 bit here is given by:
1310c3cdd6e4SMartin Kepplinger 		 *	g * N * 1000000 / 2048 for N = 2, 4, 8 and g=9.80665
1311c3cdd6e4SMartin Kepplinger 		 */
1312244a93f6SMartin Kepplinger 		.mma_scales = { {0, 2394}, {0, 4788}, {0, 9577} },
1313605f72deSHarinath Nampally 		/*
1314605f72deSHarinath Nampally 		 * Although we enable the interrupt sources once and for
1315605f72deSHarinath Nampally 		 * all here the event detection itself is not enabled until
1316605f72deSHarinath Nampally 		 * userspace asks for it by mma8452_write_event_config()
1317605f72deSHarinath Nampally 		 */
1318605f72deSHarinath Nampally 		.all_events = MMA8452_INT_DRDY |
1319605f72deSHarinath Nampally 					MMA8452_INT_TRANS |
1320605f72deSHarinath Nampally 					MMA8452_INT_FF_MT,
1321605f72deSHarinath Nampally 		.enabled_events = MMA8452_INT_TRANS |
1322605f72deSHarinath Nampally 					MMA8452_INT_FF_MT,
1323244a93f6SMartin Kepplinger 	},
1324244a93f6SMartin Kepplinger 	[mma8452] = {
1325244a93f6SMartin Kepplinger 		.chip_id = MMA8452_DEVICE_ID,
1326244a93f6SMartin Kepplinger 		.channels = mma8452_channels,
1327244a93f6SMartin Kepplinger 		.num_channels = ARRAY_SIZE(mma8452_channels),
1328c3cdd6e4SMartin Kepplinger 		.mma_scales = { {0, 9577}, {0, 19154}, {0, 38307} },
1329605f72deSHarinath Nampally 		/*
1330605f72deSHarinath Nampally 		 * Although we enable the interrupt sources once and for
1331605f72deSHarinath Nampally 		 * all here the event detection itself is not enabled until
1332605f72deSHarinath Nampally 		 * userspace asks for it by mma8452_write_event_config()
1333605f72deSHarinath Nampally 		 */
1334605f72deSHarinath Nampally 		.all_events = MMA8452_INT_DRDY |
1335605f72deSHarinath Nampally 					MMA8452_INT_TRANS |
1336605f72deSHarinath Nampally 					MMA8452_INT_FF_MT,
1337605f72deSHarinath Nampally 		.enabled_events = MMA8452_INT_TRANS |
1338605f72deSHarinath Nampally 					MMA8452_INT_FF_MT,
1339c3cdd6e4SMartin Kepplinger 	},
1340c5ea1b58SMartin Kepplinger 	[mma8453] = {
1341c5ea1b58SMartin Kepplinger 		.chip_id = MMA8453_DEVICE_ID,
1342c5ea1b58SMartin Kepplinger 		.channels = mma8453_channels,
1343c5ea1b58SMartin Kepplinger 		.num_channels = ARRAY_SIZE(mma8453_channels),
1344c5ea1b58SMartin Kepplinger 		.mma_scales = { {0, 38307}, {0, 76614}, {0, 153228} },
1345605f72deSHarinath Nampally 		/*
1346605f72deSHarinath Nampally 		 * Although we enable the interrupt sources once and for
1347605f72deSHarinath Nampally 		 * all here the event detection itself is not enabled until
1348605f72deSHarinath Nampally 		 * userspace asks for it by mma8452_write_event_config()
1349605f72deSHarinath Nampally 		 */
1350605f72deSHarinath Nampally 		.all_events = MMA8452_INT_DRDY |
1351605f72deSHarinath Nampally 					MMA8452_INT_TRANS |
1352605f72deSHarinath Nampally 					MMA8452_INT_FF_MT,
1353605f72deSHarinath Nampally 		.enabled_events = MMA8452_INT_TRANS |
1354605f72deSHarinath Nampally 					MMA8452_INT_FF_MT,
1355c5ea1b58SMartin Kepplinger 	},
1356417e008bSMartin Kepplinger 	[mma8652] = {
1357417e008bSMartin Kepplinger 		.chip_id = MMA8652_DEVICE_ID,
1358417e008bSMartin Kepplinger 		.channels = mma8652_channels,
1359417e008bSMartin Kepplinger 		.num_channels = ARRAY_SIZE(mma8652_channels),
1360417e008bSMartin Kepplinger 		.mma_scales = { {0, 9577}, {0, 19154}, {0, 38307} },
1361605f72deSHarinath Nampally 		.all_events = MMA8452_INT_DRDY |
1362605f72deSHarinath Nampally 					MMA8452_INT_FF_MT,
1363605f72deSHarinath Nampally 		.enabled_events = MMA8452_INT_FF_MT,
1364417e008bSMartin Kepplinger 	},
1365417e008bSMartin Kepplinger 	[mma8653] = {
1366417e008bSMartin Kepplinger 		.chip_id = MMA8653_DEVICE_ID,
1367417e008bSMartin Kepplinger 		.channels = mma8653_channels,
1368417e008bSMartin Kepplinger 		.num_channels = ARRAY_SIZE(mma8653_channels),
1369417e008bSMartin 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_FF_MT,
1377605f72deSHarinath Nampally 		.enabled_events = MMA8452_INT_FF_MT,
1378417e008bSMartin Kepplinger 	},
1379e8731180SMartin Kepplinger 	[fxls8471] = {
1380e8731180SMartin Kepplinger 		.chip_id = FXLS8471_DEVICE_ID,
1381e8731180SMartin Kepplinger 		.channels = mma8451_channels,
1382e8731180SMartin Kepplinger 		.num_channels = ARRAY_SIZE(mma8451_channels),
1383e8731180SMartin Kepplinger 		.mma_scales = { {0, 2394}, {0, 4788}, {0, 9577} },
1384605f72deSHarinath Nampally 		/*
1385605f72deSHarinath Nampally 		 * Although we enable the interrupt sources once and for
1386605f72deSHarinath Nampally 		 * all here the event detection itself is not enabled until
1387605f72deSHarinath Nampally 		 * userspace asks for it by mma8452_write_event_config()
1388605f72deSHarinath Nampally 		 */
1389605f72deSHarinath Nampally 		.all_events = MMA8452_INT_DRDY |
1390605f72deSHarinath Nampally 					MMA8452_INT_TRANS |
1391605f72deSHarinath Nampally 					MMA8452_INT_FF_MT,
1392605f72deSHarinath Nampally 		.enabled_events = MMA8452_INT_TRANS |
1393605f72deSHarinath Nampally 					MMA8452_INT_FF_MT,
1394e8731180SMartin Kepplinger 	},
1395c3cdd6e4SMartin Kepplinger };
1396c3cdd6e4SMartin Kepplinger 
1397c7eeea93SPeter Meerwald static struct attribute *mma8452_attributes[] = {
1398c7eeea93SPeter Meerwald 	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
1399c7eeea93SPeter Meerwald 	&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
14001e79841aSMartin Fuzzey 	&iio_dev_attr_in_accel_filter_high_pass_3db_frequency_available.dev_attr.attr,
1401ed859fc1SMartin Kepplinger 	&iio_dev_attr_in_accel_oversampling_ratio_available.dev_attr.attr,
1402c7eeea93SPeter Meerwald 	NULL
1403c7eeea93SPeter Meerwald };
1404c7eeea93SPeter Meerwald 
1405c7eeea93SPeter Meerwald static const struct attribute_group mma8452_group = {
1406c7eeea93SPeter Meerwald 	.attrs = mma8452_attributes,
1407c7eeea93SPeter Meerwald };
1408c7eeea93SPeter Meerwald 
1409c7eeea93SPeter Meerwald static const struct iio_info mma8452_info = {
1410c7eeea93SPeter Meerwald 	.attrs = &mma8452_group,
1411c7eeea93SPeter Meerwald 	.read_raw = &mma8452_read_raw,
1412c7eeea93SPeter Meerwald 	.write_raw = &mma8452_write_raw,
141328e34278SMartin Fuzzey 	.event_attrs = &mma8452_event_attribute_group,
14144febd9f1SHarinath Nampally 	.read_event_value = &mma8452_read_event_value,
14154febd9f1SHarinath Nampally 	.write_event_value = &mma8452_write_event_value,
141628e34278SMartin Fuzzey 	.read_event_config = &mma8452_read_event_config,
141728e34278SMartin Fuzzey 	.write_event_config = &mma8452_write_event_config,
14182a17698cSMartin Fuzzey 	.debugfs_reg_access = &mma8452_reg_access_dbg,
1419c7eeea93SPeter Meerwald };
1420c7eeea93SPeter Meerwald 
1421c7eeea93SPeter Meerwald static const unsigned long mma8452_scan_masks[] = {0x7, 0};
1422c7eeea93SPeter Meerwald 
1423ae6d9ce0SMartin Fuzzey static int mma8452_data_rdy_trigger_set_state(struct iio_trigger *trig,
1424ae6d9ce0SMartin Fuzzey 					      bool state)
1425ae6d9ce0SMartin Fuzzey {
1426ae6d9ce0SMartin Fuzzey 	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
1427ae6d9ce0SMartin Fuzzey 	struct mma8452_data *data = iio_priv(indio_dev);
142896c0cb2bSMartin Kepplinger 	int reg, ret;
142996c0cb2bSMartin Kepplinger 
143096c0cb2bSMartin Kepplinger 	ret = mma8452_set_runtime_pm_state(data->client, state);
143196c0cb2bSMartin Kepplinger 	if (ret)
143296c0cb2bSMartin Kepplinger 		return ret;
1433ae6d9ce0SMartin Fuzzey 
1434ae6d9ce0SMartin Fuzzey 	reg = i2c_smbus_read_byte_data(data->client, MMA8452_CTRL_REG4);
1435ae6d9ce0SMartin Fuzzey 	if (reg < 0)
1436ae6d9ce0SMartin Fuzzey 		return reg;
1437ae6d9ce0SMartin Fuzzey 
1438ae6d9ce0SMartin Fuzzey 	if (state)
1439ae6d9ce0SMartin Fuzzey 		reg |= MMA8452_INT_DRDY;
1440ae6d9ce0SMartin Fuzzey 	else
1441ae6d9ce0SMartin Fuzzey 		reg &= ~MMA8452_INT_DRDY;
1442ae6d9ce0SMartin Fuzzey 
1443ae6d9ce0SMartin Fuzzey 	return mma8452_change_config(data, MMA8452_CTRL_REG4, reg);
1444ae6d9ce0SMartin Fuzzey }
1445ae6d9ce0SMartin Fuzzey 
1446ae6d9ce0SMartin Fuzzey static const struct iio_trigger_ops mma8452_trigger_ops = {
1447ae6d9ce0SMartin Fuzzey 	.set_trigger_state = mma8452_data_rdy_trigger_set_state,
144819808e04SLars-Peter Clausen 	.validate_device = iio_trigger_validate_own_device,
1449ae6d9ce0SMartin Fuzzey };
1450ae6d9ce0SMartin Fuzzey 
1451ae6d9ce0SMartin Fuzzey static int mma8452_trigger_setup(struct iio_dev *indio_dev)
1452ae6d9ce0SMartin Fuzzey {
1453ae6d9ce0SMartin Fuzzey 	struct mma8452_data *data = iio_priv(indio_dev);
1454ae6d9ce0SMartin Fuzzey 	struct iio_trigger *trig;
1455ae6d9ce0SMartin Fuzzey 	int ret;
1456ae6d9ce0SMartin Fuzzey 
1457ae6d9ce0SMartin Fuzzey 	trig = devm_iio_trigger_alloc(&data->client->dev, "%s-dev%d",
1458ae6d9ce0SMartin Fuzzey 				      indio_dev->name,
1459ae6d9ce0SMartin Fuzzey 				      indio_dev->id);
1460ae6d9ce0SMartin Fuzzey 	if (!trig)
1461ae6d9ce0SMartin Fuzzey 		return -ENOMEM;
1462ae6d9ce0SMartin Fuzzey 
1463ae6d9ce0SMartin Fuzzey 	trig->dev.parent = &data->client->dev;
1464ae6d9ce0SMartin Fuzzey 	trig->ops = &mma8452_trigger_ops;
1465ae6d9ce0SMartin Fuzzey 	iio_trigger_set_drvdata(trig, indio_dev);
1466ae6d9ce0SMartin Fuzzey 
1467ae6d9ce0SMartin Fuzzey 	ret = iio_trigger_register(trig);
1468ae6d9ce0SMartin Fuzzey 	if (ret)
1469ae6d9ce0SMartin Fuzzey 		return ret;
1470ae6d9ce0SMartin Fuzzey 
1471ae6d9ce0SMartin Fuzzey 	indio_dev->trig = trig;
1472686027fbSHartmut Knaack 
1473ae6d9ce0SMartin Fuzzey 	return 0;
1474ae6d9ce0SMartin Fuzzey }
1475ae6d9ce0SMartin Fuzzey 
1476ae6d9ce0SMartin Fuzzey static void mma8452_trigger_cleanup(struct iio_dev *indio_dev)
1477ae6d9ce0SMartin Fuzzey {
1478ae6d9ce0SMartin Fuzzey 	if (indio_dev->trig)
1479ae6d9ce0SMartin Fuzzey 		iio_trigger_unregister(indio_dev->trig);
1480ae6d9ce0SMartin Fuzzey }
1481ae6d9ce0SMartin Fuzzey 
1482ecabae71SMartin Fuzzey static int mma8452_reset(struct i2c_client *client)
1483ecabae71SMartin Fuzzey {
1484ecabae71SMartin Fuzzey 	int i;
1485ecabae71SMartin Fuzzey 	int ret;
1486ecabae71SMartin Fuzzey 
1487ecabae71SMartin Fuzzey 	ret = i2c_smbus_write_byte_data(client,	MMA8452_CTRL_REG2,
1488ecabae71SMartin Fuzzey 					MMA8452_CTRL_REG2_RST);
1489ecabae71SMartin Fuzzey 	if (ret < 0)
1490ecabae71SMartin Fuzzey 		return ret;
1491ecabae71SMartin Fuzzey 
1492ecabae71SMartin Fuzzey 	for (i = 0; i < 10; i++) {
1493ecabae71SMartin Fuzzey 		usleep_range(100, 200);
1494ecabae71SMartin Fuzzey 		ret = i2c_smbus_read_byte_data(client, MMA8452_CTRL_REG2);
1495ecabae71SMartin Fuzzey 		if (ret == -EIO)
1496ecabae71SMartin Fuzzey 			continue; /* I2C comm reset */
1497ecabae71SMartin Fuzzey 		if (ret < 0)
1498ecabae71SMartin Fuzzey 			return ret;
1499ecabae71SMartin Fuzzey 		if (!(ret & MMA8452_CTRL_REG2_RST))
1500ecabae71SMartin Fuzzey 			return 0;
1501ecabae71SMartin Fuzzey 	}
1502ecabae71SMartin Fuzzey 
1503ecabae71SMartin Fuzzey 	return -ETIMEDOUT;
1504ecabae71SMartin Fuzzey }
1505ecabae71SMartin Fuzzey 
1506c3cdd6e4SMartin Kepplinger static const struct of_device_id mma8452_dt_ids[] = {
1507244a93f6SMartin Kepplinger 	{ .compatible = "fsl,mma8451", .data = &mma_chip_info_table[mma8451] },
1508c3cdd6e4SMartin Kepplinger 	{ .compatible = "fsl,mma8452", .data = &mma_chip_info_table[mma8452] },
1509c5ea1b58SMartin Kepplinger 	{ .compatible = "fsl,mma8453", .data = &mma_chip_info_table[mma8453] },
1510417e008bSMartin Kepplinger 	{ .compatible = "fsl,mma8652", .data = &mma_chip_info_table[mma8652] },
1511417e008bSMartin Kepplinger 	{ .compatible = "fsl,mma8653", .data = &mma_chip_info_table[mma8653] },
1512e8731180SMartin Kepplinger 	{ .compatible = "fsl,fxls8471", .data = &mma_chip_info_table[fxls8471] },
1513c3cdd6e4SMartin Kepplinger 	{ }
1514c3cdd6e4SMartin Kepplinger };
1515c3cdd6e4SMartin Kepplinger MODULE_DEVICE_TABLE(of, mma8452_dt_ids);
1516c3cdd6e4SMartin Kepplinger 
1517c7eeea93SPeter Meerwald static int mma8452_probe(struct i2c_client *client,
1518c7eeea93SPeter Meerwald 			 const struct i2c_device_id *id)
1519c7eeea93SPeter Meerwald {
1520c7eeea93SPeter Meerwald 	struct mma8452_data *data;
1521c7eeea93SPeter Meerwald 	struct iio_dev *indio_dev;
1522c7eeea93SPeter Meerwald 	int ret;
1523c3cdd6e4SMartin Kepplinger 	const struct of_device_id *match;
1524c7eeea93SPeter Meerwald 
1525c3cdd6e4SMartin Kepplinger 	match = of_match_device(mma8452_dt_ids, &client->dev);
1526c3cdd6e4SMartin Kepplinger 	if (!match) {
1527c3cdd6e4SMartin Kepplinger 		dev_err(&client->dev, "unknown device model\n");
1528c3cdd6e4SMartin Kepplinger 		return -ENODEV;
1529c3cdd6e4SMartin Kepplinger 	}
1530c3cdd6e4SMartin Kepplinger 
1531c7eeea93SPeter Meerwald 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
1532c7eeea93SPeter Meerwald 	if (!indio_dev)
1533c7eeea93SPeter Meerwald 		return -ENOMEM;
1534c7eeea93SPeter Meerwald 
1535c7eeea93SPeter Meerwald 	data = iio_priv(indio_dev);
1536c7eeea93SPeter Meerwald 	data->client = client;
1537c7eeea93SPeter Meerwald 	mutex_init(&data->lock);
1538c3cdd6e4SMartin Kepplinger 	data->chip_info = match->data;
1539c3cdd6e4SMartin Kepplinger 
1540f6ff49b8SAnson Huang 	data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
1541f6ff49b8SAnson Huang 	if (IS_ERR(data->vdd_reg)) {
1542f6ff49b8SAnson Huang 		if (PTR_ERR(data->vdd_reg) == -EPROBE_DEFER)
1543f6ff49b8SAnson Huang 			return -EPROBE_DEFER;
1544f6ff49b8SAnson Huang 
1545f6ff49b8SAnson Huang 		dev_err(&client->dev, "failed to get VDD regulator!\n");
1546f6ff49b8SAnson Huang 		return PTR_ERR(data->vdd_reg);
1547f6ff49b8SAnson Huang 	}
1548f6ff49b8SAnson Huang 
1549f6ff49b8SAnson Huang 	data->vddio_reg = devm_regulator_get(&client->dev, "vddio");
1550f6ff49b8SAnson Huang 	if (IS_ERR(data->vddio_reg)) {
1551f6ff49b8SAnson Huang 		if (PTR_ERR(data->vddio_reg) == -EPROBE_DEFER)
1552f6ff49b8SAnson Huang 			return -EPROBE_DEFER;
1553f6ff49b8SAnson Huang 
1554f6ff49b8SAnson Huang 		dev_err(&client->dev, "failed to get VDDIO regulator!\n");
1555f6ff49b8SAnson Huang 		return PTR_ERR(data->vddio_reg);
1556f6ff49b8SAnson Huang 	}
1557f6ff49b8SAnson Huang 
1558f6ff49b8SAnson Huang 	ret = regulator_enable(data->vdd_reg);
1559f6ff49b8SAnson Huang 	if (ret) {
1560f6ff49b8SAnson Huang 		dev_err(&client->dev, "failed to enable VDD regulator!\n");
1561f6ff49b8SAnson Huang 		return ret;
1562f6ff49b8SAnson Huang 	}
1563f6ff49b8SAnson Huang 
1564f6ff49b8SAnson Huang 	ret = regulator_enable(data->vddio_reg);
1565f6ff49b8SAnson Huang 	if (ret) {
1566f6ff49b8SAnson Huang 		dev_err(&client->dev, "failed to enable VDDIO regulator!\n");
1567f6ff49b8SAnson Huang 		goto disable_regulator_vdd;
1568f6ff49b8SAnson Huang 	}
1569f6ff49b8SAnson Huang 
1570417e008bSMartin Kepplinger 	ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I);
1571417e008bSMartin Kepplinger 	if (ret < 0)
1572f6ff49b8SAnson Huang 		goto disable_regulators;
1573417e008bSMartin Kepplinger 
1574417e008bSMartin Kepplinger 	switch (ret) {
1575244a93f6SMartin Kepplinger 	case MMA8451_DEVICE_ID:
1576417e008bSMartin Kepplinger 	case MMA8452_DEVICE_ID:
1577417e008bSMartin Kepplinger 	case MMA8453_DEVICE_ID:
1578417e008bSMartin Kepplinger 	case MMA8652_DEVICE_ID:
1579417e008bSMartin Kepplinger 	case MMA8653_DEVICE_ID:
1580e8731180SMartin Kepplinger 	case FXLS8471_DEVICE_ID:
1581417e008bSMartin Kepplinger 		if (ret == data->chip_info->chip_id)
1582417e008bSMartin Kepplinger 			break;
1583cb57f2ebSGustavo A. R. Silva 		/* fall through */
1584417e008bSMartin Kepplinger 	default:
1585f6ff49b8SAnson Huang 		ret = -ENODEV;
1586f6ff49b8SAnson Huang 		goto disable_regulators;
1587417e008bSMartin Kepplinger 	}
1588417e008bSMartin Kepplinger 
1589c3cdd6e4SMartin Kepplinger 	dev_info(&client->dev, "registering %s accelerometer; ID 0x%x\n",
1590c3cdd6e4SMartin Kepplinger 		 match->compatible, data->chip_info->chip_id);
1591c7eeea93SPeter Meerwald 
1592c7eeea93SPeter Meerwald 	i2c_set_clientdata(client, indio_dev);
1593c7eeea93SPeter Meerwald 	indio_dev->info = &mma8452_info;
1594c7eeea93SPeter Meerwald 	indio_dev->name = id->name;
1595c7eeea93SPeter Meerwald 	indio_dev->dev.parent = &client->dev;
1596c7eeea93SPeter Meerwald 	indio_dev->modes = INDIO_DIRECT_MODE;
1597c3cdd6e4SMartin Kepplinger 	indio_dev->channels = data->chip_info->channels;
1598c3cdd6e4SMartin Kepplinger 	indio_dev->num_channels = data->chip_info->num_channels;
1599c7eeea93SPeter Meerwald 	indio_dev->available_scan_masks = mma8452_scan_masks;
1600c7eeea93SPeter Meerwald 
1601ecabae71SMartin Fuzzey 	ret = mma8452_reset(client);
1602c7eeea93SPeter Meerwald 	if (ret < 0)
1603f6ff49b8SAnson Huang 		goto disable_regulators;
1604c7eeea93SPeter Meerwald 
1605c7eeea93SPeter Meerwald 	data->data_cfg = MMA8452_DATA_CFG_FS_2G;
1606c7eeea93SPeter Meerwald 	ret = i2c_smbus_write_byte_data(client, MMA8452_DATA_CFG,
1607c7eeea93SPeter Meerwald 					data->data_cfg);
1608c7eeea93SPeter Meerwald 	if (ret < 0)
1609f6ff49b8SAnson Huang 		goto disable_regulators;
1610c7eeea93SPeter Meerwald 
161128e34278SMartin Fuzzey 	/*
161228e34278SMartin Fuzzey 	 * By default set transient threshold to max to avoid events if
161328e34278SMartin Fuzzey 	 * enabling without configuring threshold.
161428e34278SMartin Fuzzey 	 */
161528e34278SMartin Fuzzey 	ret = i2c_smbus_write_byte_data(client, MMA8452_TRANSIENT_THS,
161628e34278SMartin Fuzzey 					MMA8452_TRANSIENT_THS_MASK);
161728e34278SMartin Fuzzey 	if (ret < 0)
1618f6ff49b8SAnson Huang 		goto disable_regulators;
161928e34278SMartin Fuzzey 
162028e34278SMartin Fuzzey 	if (client->irq) {
1621d2a3e093SMartin Kepplinger 		int irq2;
162228e34278SMartin Fuzzey 
1623d2a3e093SMartin Kepplinger 		irq2 = of_irq_get_byname(client->dev.of_node, "INT2");
1624d2a3e093SMartin Kepplinger 
1625d2a3e093SMartin Kepplinger 		if (irq2 == client->irq) {
1626d2a3e093SMartin Kepplinger 			dev_dbg(&client->dev, "using interrupt line INT2\n");
1627d2a3e093SMartin Kepplinger 		} else {
162828e34278SMartin Fuzzey 			ret = i2c_smbus_write_byte_data(client,
162928e34278SMartin Fuzzey 						MMA8452_CTRL_REG5,
1630605f72deSHarinath Nampally 						data->chip_info->all_events);
163128e34278SMartin Fuzzey 			if (ret < 0)
1632f6ff49b8SAnson Huang 				goto disable_regulators;
163328e34278SMartin Fuzzey 
1634d2a3e093SMartin Kepplinger 			dev_dbg(&client->dev, "using interrupt line INT1\n");
1635d2a3e093SMartin Kepplinger 		}
1636d2a3e093SMartin Kepplinger 
163728e34278SMartin Fuzzey 		ret = i2c_smbus_write_byte_data(client,
163828e34278SMartin Fuzzey 					MMA8452_CTRL_REG4,
1639605f72deSHarinath Nampally 					data->chip_info->enabled_events);
1640ae6d9ce0SMartin Fuzzey 		if (ret < 0)
1641f6ff49b8SAnson Huang 			goto disable_regulators;
1642ae6d9ce0SMartin Fuzzey 
1643ae6d9ce0SMartin Fuzzey 		ret = mma8452_trigger_setup(indio_dev);
164428e34278SMartin Fuzzey 		if (ret < 0)
1645f6ff49b8SAnson Huang 			goto disable_regulators;
164628e34278SMartin Fuzzey 	}
164728e34278SMartin Fuzzey 
1648ecabae71SMartin Fuzzey 	data->ctrl_reg1 = MMA8452_CTRL_ACTIVE |
1649ecabae71SMartin Fuzzey 			  (MMA8452_CTRL_DR_DEFAULT << MMA8452_CTRL_DR_SHIFT);
1650a45d1238SRichard Tresidder 
1651a45d1238SRichard Tresidder 	data->sleep_val = mma8452_calculate_sleep(data);
1652a45d1238SRichard Tresidder 
1653ecabae71SMartin Fuzzey 	ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1,
1654ecabae71SMartin Fuzzey 					data->ctrl_reg1);
1655ecabae71SMartin Fuzzey 	if (ret < 0)
1656ae6d9ce0SMartin Fuzzey 		goto trigger_cleanup;
1657ecabae71SMartin Fuzzey 
1658c7eeea93SPeter Meerwald 	ret = iio_triggered_buffer_setup(indio_dev, NULL,
1659c7eeea93SPeter Meerwald 					 mma8452_trigger_handler, NULL);
1660c7eeea93SPeter Meerwald 	if (ret < 0)
1661ae6d9ce0SMartin Fuzzey 		goto trigger_cleanup;
1662c7eeea93SPeter Meerwald 
166328e34278SMartin Fuzzey 	if (client->irq) {
166428e34278SMartin Fuzzey 		ret = devm_request_threaded_irq(&client->dev,
166528e34278SMartin Fuzzey 						client->irq,
166628e34278SMartin Fuzzey 						NULL, mma8452_interrupt,
166728e34278SMartin Fuzzey 						IRQF_TRIGGER_LOW | IRQF_ONESHOT,
166828e34278SMartin Fuzzey 						client->name, indio_dev);
166928e34278SMartin Fuzzey 		if (ret)
167028e34278SMartin Fuzzey 			goto buffer_cleanup;
167128e34278SMartin Fuzzey 	}
167228e34278SMartin Fuzzey 
167396c0cb2bSMartin Kepplinger 	ret = pm_runtime_set_active(&client->dev);
167496c0cb2bSMartin Kepplinger 	if (ret < 0)
167596c0cb2bSMartin Kepplinger 		goto buffer_cleanup;
167696c0cb2bSMartin Kepplinger 
167796c0cb2bSMartin Kepplinger 	pm_runtime_enable(&client->dev);
167896c0cb2bSMartin Kepplinger 	pm_runtime_set_autosuspend_delay(&client->dev,
167996c0cb2bSMartin Kepplinger 					 MMA8452_AUTO_SUSPEND_DELAY_MS);
168096c0cb2bSMartin Kepplinger 	pm_runtime_use_autosuspend(&client->dev);
168196c0cb2bSMartin Kepplinger 
1682c7eeea93SPeter Meerwald 	ret = iio_device_register(indio_dev);
1683c7eeea93SPeter Meerwald 	if (ret < 0)
1684c7eeea93SPeter Meerwald 		goto buffer_cleanup;
168528e34278SMartin Fuzzey 
16864b04266aSMartin Kepplinger 	ret = mma8452_set_freefall_mode(data, false);
16871a965d40SBijosh Thykkoottathil 	if (ret < 0)
1688*d7369ae1SChuhong Yuan 		goto unregister_device;
16894b04266aSMartin Kepplinger 
1690c7eeea93SPeter Meerwald 	return 0;
1691c7eeea93SPeter Meerwald 
1692*d7369ae1SChuhong Yuan unregister_device:
1693*d7369ae1SChuhong Yuan 	iio_device_unregister(indio_dev);
1694*d7369ae1SChuhong Yuan 
1695c7eeea93SPeter Meerwald buffer_cleanup:
1696c7eeea93SPeter Meerwald 	iio_triggered_buffer_cleanup(indio_dev);
1697ae6d9ce0SMartin Fuzzey 
1698ae6d9ce0SMartin Fuzzey trigger_cleanup:
1699ae6d9ce0SMartin Fuzzey 	mma8452_trigger_cleanup(indio_dev);
1700ae6d9ce0SMartin Fuzzey 
1701f6ff49b8SAnson Huang disable_regulators:
1702f6ff49b8SAnson Huang 	regulator_disable(data->vddio_reg);
1703f6ff49b8SAnson Huang 
1704f6ff49b8SAnson Huang disable_regulator_vdd:
1705f6ff49b8SAnson Huang 	regulator_disable(data->vdd_reg);
1706f6ff49b8SAnson Huang 
1707c7eeea93SPeter Meerwald 	return ret;
1708c7eeea93SPeter Meerwald }
1709c7eeea93SPeter Meerwald 
1710c7eeea93SPeter Meerwald static int mma8452_remove(struct i2c_client *client)
1711c7eeea93SPeter Meerwald {
1712c7eeea93SPeter Meerwald 	struct iio_dev *indio_dev = i2c_get_clientdata(client);
1713f6ff49b8SAnson Huang 	struct mma8452_data *data = iio_priv(indio_dev);
1714c7eeea93SPeter Meerwald 
1715c7eeea93SPeter Meerwald 	iio_device_unregister(indio_dev);
171696c0cb2bSMartin Kepplinger 
171796c0cb2bSMartin Kepplinger 	pm_runtime_disable(&client->dev);
171896c0cb2bSMartin Kepplinger 	pm_runtime_set_suspended(&client->dev);
171996c0cb2bSMartin Kepplinger 	pm_runtime_put_noidle(&client->dev);
172096c0cb2bSMartin Kepplinger 
1721c7eeea93SPeter Meerwald 	iio_triggered_buffer_cleanup(indio_dev);
1722ae6d9ce0SMartin Fuzzey 	mma8452_trigger_cleanup(indio_dev);
1723c7eeea93SPeter Meerwald 	mma8452_standby(iio_priv(indio_dev));
1724c7eeea93SPeter Meerwald 
1725f6ff49b8SAnson Huang 	regulator_disable(data->vddio_reg);
1726f6ff49b8SAnson Huang 	regulator_disable(data->vdd_reg);
1727f6ff49b8SAnson Huang 
1728c7eeea93SPeter Meerwald 	return 0;
1729c7eeea93SPeter Meerwald }
1730c7eeea93SPeter Meerwald 
173196c0cb2bSMartin Kepplinger #ifdef CONFIG_PM
173296c0cb2bSMartin Kepplinger static int mma8452_runtime_suspend(struct device *dev)
173396c0cb2bSMartin Kepplinger {
173496c0cb2bSMartin Kepplinger 	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
173596c0cb2bSMartin Kepplinger 	struct mma8452_data *data = iio_priv(indio_dev);
173696c0cb2bSMartin Kepplinger 	int ret;
173796c0cb2bSMartin Kepplinger 
173896c0cb2bSMartin Kepplinger 	mutex_lock(&data->lock);
173996c0cb2bSMartin Kepplinger 	ret = mma8452_standby(data);
174096c0cb2bSMartin Kepplinger 	mutex_unlock(&data->lock);
174196c0cb2bSMartin Kepplinger 	if (ret < 0) {
174296c0cb2bSMartin Kepplinger 		dev_err(&data->client->dev, "powering off device failed\n");
174396c0cb2bSMartin Kepplinger 		return -EAGAIN;
174496c0cb2bSMartin Kepplinger 	}
174596c0cb2bSMartin Kepplinger 
1746f6ff49b8SAnson Huang 	ret = regulator_disable(data->vddio_reg);
1747f6ff49b8SAnson Huang 	if (ret) {
1748f6ff49b8SAnson Huang 		dev_err(dev, "failed to disable VDDIO regulator\n");
1749f6ff49b8SAnson Huang 		return ret;
1750f6ff49b8SAnson Huang 	}
1751f6ff49b8SAnson Huang 
1752f6ff49b8SAnson Huang 	ret = regulator_disable(data->vdd_reg);
1753f6ff49b8SAnson Huang 	if (ret) {
1754f6ff49b8SAnson Huang 		dev_err(dev, "failed to disable VDD regulator\n");
1755f6ff49b8SAnson Huang 		return ret;
1756f6ff49b8SAnson Huang 	}
1757f6ff49b8SAnson Huang 
175896c0cb2bSMartin Kepplinger 	return 0;
175996c0cb2bSMartin Kepplinger }
176096c0cb2bSMartin Kepplinger 
176196c0cb2bSMartin Kepplinger static int mma8452_runtime_resume(struct device *dev)
176296c0cb2bSMartin Kepplinger {
176396c0cb2bSMartin Kepplinger 	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
176496c0cb2bSMartin Kepplinger 	struct mma8452_data *data = iio_priv(indio_dev);
176596c0cb2bSMartin Kepplinger 	int ret, sleep_val;
176696c0cb2bSMartin Kepplinger 
1767f6ff49b8SAnson Huang 	ret = regulator_enable(data->vdd_reg);
1768f6ff49b8SAnson Huang 	if (ret) {
1769f6ff49b8SAnson Huang 		dev_err(dev, "failed to enable VDD regulator\n");
1770f6ff49b8SAnson Huang 		return ret;
1771f6ff49b8SAnson Huang 	}
1772f6ff49b8SAnson Huang 
1773f6ff49b8SAnson Huang 	ret = regulator_enable(data->vddio_reg);
1774f6ff49b8SAnson Huang 	if (ret) {
1775f6ff49b8SAnson Huang 		dev_err(dev, "failed to enable VDDIO regulator\n");
1776f6ff49b8SAnson Huang 		regulator_disable(data->vdd_reg);
1777f6ff49b8SAnson Huang 		return ret;
1778f6ff49b8SAnson Huang 	}
1779f6ff49b8SAnson Huang 
178096c0cb2bSMartin Kepplinger 	ret = mma8452_active(data);
178196c0cb2bSMartin Kepplinger 	if (ret < 0)
1782f6ff49b8SAnson Huang 		goto runtime_resume_failed;
178396c0cb2bSMartin Kepplinger 
178496c0cb2bSMartin Kepplinger 	ret = mma8452_get_odr_index(data);
178596c0cb2bSMartin Kepplinger 	sleep_val = 1000 / mma8452_samp_freq[ret][0];
178696c0cb2bSMartin Kepplinger 	if (sleep_val < 20)
178796c0cb2bSMartin Kepplinger 		usleep_range(sleep_val * 1000, 20000);
178896c0cb2bSMartin Kepplinger 	else
178996c0cb2bSMartin Kepplinger 		msleep_interruptible(sleep_val);
179096c0cb2bSMartin Kepplinger 
179196c0cb2bSMartin Kepplinger 	return 0;
179296c0cb2bSMartin Kepplinger 
1793f6ff49b8SAnson Huang runtime_resume_failed:
1794f6ff49b8SAnson Huang 	regulator_disable(data->vddio_reg);
1795f6ff49b8SAnson Huang 	regulator_disable(data->vdd_reg);
1796c7eeea93SPeter Meerwald 
1797f6ff49b8SAnson Huang 	return ret;
1798c7eeea93SPeter Meerwald }
1799c7eeea93SPeter Meerwald #endif
1800c7eeea93SPeter Meerwald 
180196c0cb2bSMartin Kepplinger static const struct dev_pm_ops mma8452_pm_ops = {
1802f6ff49b8SAnson Huang 	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
180396c0cb2bSMartin Kepplinger 	SET_RUNTIME_PM_OPS(mma8452_runtime_suspend,
180496c0cb2bSMartin Kepplinger 			   mma8452_runtime_resume, NULL)
180596c0cb2bSMartin Kepplinger };
180696c0cb2bSMartin Kepplinger 
1807c7eeea93SPeter Meerwald static const struct i2c_device_id mma8452_id[] = {
1808ddb851afSMartin Kepplinger 	{ "mma8451", mma8451 },
1809c3cdd6e4SMartin Kepplinger 	{ "mma8452", mma8452 },
1810c5ea1b58SMartin Kepplinger 	{ "mma8453", mma8453 },
1811417e008bSMartin Kepplinger 	{ "mma8652", mma8652 },
1812417e008bSMartin Kepplinger 	{ "mma8653", mma8653 },
1813e8731180SMartin Kepplinger 	{ "fxls8471", fxls8471 },
1814c7eeea93SPeter Meerwald 	{ }
1815c7eeea93SPeter Meerwald };
1816c7eeea93SPeter Meerwald MODULE_DEVICE_TABLE(i2c, mma8452_id);
1817c7eeea93SPeter Meerwald 
1818c7eeea93SPeter Meerwald static struct i2c_driver mma8452_driver = {
1819c7eeea93SPeter Meerwald 	.driver = {
1820c7eeea93SPeter Meerwald 		.name	= "mma8452",
1821a3fb96a8SMartin Fuzzey 		.of_match_table = of_match_ptr(mma8452_dt_ids),
182296c0cb2bSMartin Kepplinger 		.pm	= &mma8452_pm_ops,
1823c7eeea93SPeter Meerwald 	},
1824c7eeea93SPeter Meerwald 	.probe = mma8452_probe,
1825c7eeea93SPeter Meerwald 	.remove = mma8452_remove,
1826c7eeea93SPeter Meerwald 	.id_table = mma8452_id,
1827c7eeea93SPeter Meerwald };
1828c7eeea93SPeter Meerwald module_i2c_driver(mma8452_driver);
1829c7eeea93SPeter Meerwald 
1830c7eeea93SPeter Meerwald MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
1831f26ab1aaSMartin Kepplinger MODULE_DESCRIPTION("Freescale / NXP MMA8452 accelerometer driver");
1832c7eeea93SPeter Meerwald MODULE_LICENSE("GPL");
1833