xref: /openbmc/linux/drivers/iio/accel/adxl313_core.c (revision a7a1c60b)
1636d4463SLucas Stankus // SPDX-License-Identifier: GPL-2.0-only
2636d4463SLucas Stankus /*
3636d4463SLucas Stankus  * ADXL313 3-Axis Digital Accelerometer
4636d4463SLucas Stankus  *
5636d4463SLucas Stankus  * Copyright (c) 2021 Lucas Stankus <lucas.p.stankus@gmail.com>
6636d4463SLucas Stankus  *
7636d4463SLucas Stankus  * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL313.pdf
8636d4463SLucas Stankus  */
9636d4463SLucas Stankus 
10636d4463SLucas Stankus #include <linux/bitfield.h>
11636d4463SLucas Stankus #include <linux/module.h>
12636d4463SLucas Stankus #include <linux/regmap.h>
13636d4463SLucas Stankus 
14636d4463SLucas Stankus #include "adxl313.h"
15636d4463SLucas Stankus 
16*a7a1c60bSGeorge Mois static const struct regmap_range adxl312_readable_reg_range[] = {
17*a7a1c60bSGeorge Mois 	regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_DEVID0),
18*a7a1c60bSGeorge Mois 	regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),
19*a7a1c60bSGeorge Mois 	regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL),
20*a7a1c60bSGeorge Mois 	regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_FIFO_STATUS),
21*a7a1c60bSGeorge Mois };
22*a7a1c60bSGeorge Mois 
23636d4463SLucas Stankus static const struct regmap_range adxl313_readable_reg_range[] = {
24636d4463SLucas Stankus 	regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_XID),
25636d4463SLucas Stankus 	regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET),
26636d4463SLucas Stankus 	regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),
27636d4463SLucas Stankus 	regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL),
28636d4463SLucas Stankus 	regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_FIFO_STATUS),
29636d4463SLucas Stankus };
30636d4463SLucas Stankus 
31*a7a1c60bSGeorge Mois const struct regmap_access_table adxl312_readable_regs_table = {
32*a7a1c60bSGeorge Mois 	.yes_ranges = adxl312_readable_reg_range,
33*a7a1c60bSGeorge Mois 	.n_yes_ranges = ARRAY_SIZE(adxl312_readable_reg_range),
34*a7a1c60bSGeorge Mois };
35*a7a1c60bSGeorge Mois EXPORT_SYMBOL_NS_GPL(adxl312_readable_regs_table, IIO_ADXL313);
36*a7a1c60bSGeorge Mois 
37636d4463SLucas Stankus const struct regmap_access_table adxl313_readable_regs_table = {
38636d4463SLucas Stankus 	.yes_ranges = adxl313_readable_reg_range,
39636d4463SLucas Stankus 	.n_yes_ranges = ARRAY_SIZE(adxl313_readable_reg_range),
40636d4463SLucas Stankus };
41fa4df5a9SJonathan Cameron EXPORT_SYMBOL_NS_GPL(adxl313_readable_regs_table, IIO_ADXL313);
42636d4463SLucas Stankus 
43*a7a1c60bSGeorge Mois const struct regmap_access_table adxl314_readable_regs_table = {
44*a7a1c60bSGeorge Mois 	.yes_ranges = adxl312_readable_reg_range,
45*a7a1c60bSGeorge Mois 	.n_yes_ranges = ARRAY_SIZE(adxl312_readable_reg_range),
46*a7a1c60bSGeorge Mois };
47*a7a1c60bSGeorge Mois EXPORT_SYMBOL_NS_GPL(adxl314_readable_regs_table, IIO_ADXL313);
48*a7a1c60bSGeorge Mois 
adxl312_check_id(struct device * dev,struct adxl313_data * data)49*a7a1c60bSGeorge Mois static int adxl312_check_id(struct device *dev,
50*a7a1c60bSGeorge Mois 			    struct adxl313_data *data)
51*a7a1c60bSGeorge Mois {
52*a7a1c60bSGeorge Mois 	unsigned int regval;
53*a7a1c60bSGeorge Mois 	int ret;
54*a7a1c60bSGeorge Mois 
55*a7a1c60bSGeorge Mois 	ret = regmap_read(data->regmap, ADXL313_REG_DEVID0, &regval);
56*a7a1c60bSGeorge Mois 	if (ret)
57*a7a1c60bSGeorge Mois 		return ret;
58*a7a1c60bSGeorge Mois 
59*a7a1c60bSGeorge Mois 	if (regval != ADXL313_DEVID0_ADXL312_314)
60*a7a1c60bSGeorge Mois 		dev_warn(dev, "Invalid manufacturer ID: %#02x\n", regval);
61*a7a1c60bSGeorge Mois 
62*a7a1c60bSGeorge Mois 	return 0;
63*a7a1c60bSGeorge Mois }
64*a7a1c60bSGeorge Mois 
adxl313_check_id(struct device * dev,struct adxl313_data * data)65*a7a1c60bSGeorge Mois static int adxl313_check_id(struct device *dev,
66*a7a1c60bSGeorge Mois 			    struct adxl313_data *data)
67*a7a1c60bSGeorge Mois {
68*a7a1c60bSGeorge Mois 	unsigned int regval;
69*a7a1c60bSGeorge Mois 	int ret;
70*a7a1c60bSGeorge Mois 
71*a7a1c60bSGeorge Mois 	ret = regmap_read(data->regmap, ADXL313_REG_DEVID0, &regval);
72*a7a1c60bSGeorge Mois 	if (ret)
73*a7a1c60bSGeorge Mois 		return ret;
74*a7a1c60bSGeorge Mois 
75*a7a1c60bSGeorge Mois 	if (regval != ADXL313_DEVID0)
76*a7a1c60bSGeorge Mois 		dev_warn(dev, "Invalid manufacturer ID: 0x%02x\n", regval);
77*a7a1c60bSGeorge Mois 
78*a7a1c60bSGeorge Mois 	/* Check DEVID1 and PARTID */
79*a7a1c60bSGeorge Mois 	if (regval == ADXL313_DEVID0) {
80*a7a1c60bSGeorge Mois 		ret = regmap_read(data->regmap, ADXL313_REG_DEVID1, &regval);
81*a7a1c60bSGeorge Mois 		if (ret)
82*a7a1c60bSGeorge Mois 			return ret;
83*a7a1c60bSGeorge Mois 
84*a7a1c60bSGeorge Mois 		if (regval != ADXL313_DEVID1)
85*a7a1c60bSGeorge Mois 			dev_warn(dev, "Invalid mems ID: 0x%02x\n", regval);
86*a7a1c60bSGeorge Mois 
87*a7a1c60bSGeorge Mois 		ret = regmap_read(data->regmap, ADXL313_REG_PARTID, &regval);
88*a7a1c60bSGeorge Mois 		if (ret)
89*a7a1c60bSGeorge Mois 			return ret;
90*a7a1c60bSGeorge Mois 
91*a7a1c60bSGeorge Mois 		if (regval != ADXL313_PARTID)
92*a7a1c60bSGeorge Mois 			dev_warn(dev, "Invalid device ID: 0x%02x\n", regval);
93*a7a1c60bSGeorge Mois 	}
94*a7a1c60bSGeorge Mois 
95*a7a1c60bSGeorge Mois 	return 0;
96*a7a1c60bSGeorge Mois }
97*a7a1c60bSGeorge Mois 
98*a7a1c60bSGeorge Mois const struct adxl313_chip_info adxl31x_chip_info[] = {
99*a7a1c60bSGeorge Mois 	[ADXL312] = {
100*a7a1c60bSGeorge Mois 		.name = "adxl312",
101*a7a1c60bSGeorge Mois 		.type = ADXL312,
102*a7a1c60bSGeorge Mois 		.scale_factor = 28425072,
103*a7a1c60bSGeorge Mois 		.variable_range = true,
104*a7a1c60bSGeorge Mois 		.soft_reset = false,
105*a7a1c60bSGeorge Mois 		.check_id = &adxl312_check_id,
106*a7a1c60bSGeorge Mois 	},
107*a7a1c60bSGeorge Mois 	[ADXL313] = {
108*a7a1c60bSGeorge Mois 		.name = "adxl313",
109*a7a1c60bSGeorge Mois 		.type = ADXL313,
110*a7a1c60bSGeorge Mois 		.scale_factor = 9576806,
111*a7a1c60bSGeorge Mois 		.variable_range = true,
112*a7a1c60bSGeorge Mois 		.soft_reset = true,
113*a7a1c60bSGeorge Mois 		.check_id = &adxl313_check_id,
114*a7a1c60bSGeorge Mois 	},
115*a7a1c60bSGeorge Mois 	[ADXL314] = {
116*a7a1c60bSGeorge Mois 		.name = "adxl314",
117*a7a1c60bSGeorge Mois 		.type = ADXL314,
118*a7a1c60bSGeorge Mois 		.scale_factor = 478858719,
119*a7a1c60bSGeorge Mois 		.variable_range = false,
120*a7a1c60bSGeorge Mois 		.soft_reset = false,
121*a7a1c60bSGeorge Mois 		.check_id = &adxl312_check_id,
122*a7a1c60bSGeorge Mois 	},
123*a7a1c60bSGeorge Mois };
124*a7a1c60bSGeorge Mois EXPORT_SYMBOL_NS_GPL(adxl31x_chip_info, IIO_ADXL313);
125*a7a1c60bSGeorge Mois 
126*a7a1c60bSGeorge Mois static const struct regmap_range adxl312_writable_reg_range[] = {
127*a7a1c60bSGeorge Mois 	regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),
128*a7a1c60bSGeorge Mois 	regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL),
129*a7a1c60bSGeorge Mois 	regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_INT_MAP),
130*a7a1c60bSGeorge Mois 	regmap_reg_range(ADXL313_REG_DATA_FORMAT, ADXL313_REG_DATA_FORMAT),
131*a7a1c60bSGeorge Mois 	regmap_reg_range(ADXL313_REG_FIFO_CTL, ADXL313_REG_FIFO_CTL),
132*a7a1c60bSGeorge Mois };
133*a7a1c60bSGeorge Mois 
134636d4463SLucas Stankus static const struct regmap_range adxl313_writable_reg_range[] = {
135636d4463SLucas Stankus 	regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET),
136636d4463SLucas Stankus 	regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),
137636d4463SLucas Stankus 	regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL),
138636d4463SLucas Stankus 	regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_INT_MAP),
139636d4463SLucas Stankus 	regmap_reg_range(ADXL313_REG_DATA_FORMAT, ADXL313_REG_DATA_FORMAT),
140636d4463SLucas Stankus 	regmap_reg_range(ADXL313_REG_FIFO_CTL, ADXL313_REG_FIFO_CTL),
141636d4463SLucas Stankus };
142636d4463SLucas Stankus 
143*a7a1c60bSGeorge Mois const struct regmap_access_table adxl312_writable_regs_table = {
144*a7a1c60bSGeorge Mois 	.yes_ranges = adxl312_writable_reg_range,
145*a7a1c60bSGeorge Mois 	.n_yes_ranges = ARRAY_SIZE(adxl312_writable_reg_range),
146*a7a1c60bSGeorge Mois };
147*a7a1c60bSGeorge Mois EXPORT_SYMBOL_NS_GPL(adxl312_writable_regs_table, IIO_ADXL313);
148*a7a1c60bSGeorge Mois 
149636d4463SLucas Stankus const struct regmap_access_table adxl313_writable_regs_table = {
150636d4463SLucas Stankus 	.yes_ranges = adxl313_writable_reg_range,
151636d4463SLucas Stankus 	.n_yes_ranges = ARRAY_SIZE(adxl313_writable_reg_range),
152636d4463SLucas Stankus };
153fa4df5a9SJonathan Cameron EXPORT_SYMBOL_NS_GPL(adxl313_writable_regs_table, IIO_ADXL313);
154636d4463SLucas Stankus 
155*a7a1c60bSGeorge Mois const struct regmap_access_table adxl314_writable_regs_table = {
156*a7a1c60bSGeorge Mois 	.yes_ranges = adxl312_writable_reg_range,
157*a7a1c60bSGeorge Mois 	.n_yes_ranges = ARRAY_SIZE(adxl312_writable_reg_range),
158636d4463SLucas Stankus };
159*a7a1c60bSGeorge Mois EXPORT_SYMBOL_NS_GPL(adxl314_writable_regs_table, IIO_ADXL313);
160636d4463SLucas Stankus 
161636d4463SLucas Stankus static const int adxl313_odr_freqs[][2] = {
162636d4463SLucas Stankus 	[0] = { 6, 250000 },
163636d4463SLucas Stankus 	[1] = { 12, 500000 },
164636d4463SLucas Stankus 	[2] = { 25, 0 },
165636d4463SLucas Stankus 	[3] = { 50, 0 },
166636d4463SLucas Stankus 	[4] = { 100, 0 },
167636d4463SLucas Stankus 	[5] = { 200, 0 },
168636d4463SLucas Stankus 	[6] = { 400, 0 },
169636d4463SLucas Stankus 	[7] = { 800, 0 },
170636d4463SLucas Stankus 	[8] = { 1600, 0 },
171636d4463SLucas Stankus 	[9] = { 3200, 0 },
172636d4463SLucas Stankus };
173636d4463SLucas Stankus 
174636d4463SLucas Stankus #define ADXL313_ACCEL_CHANNEL(index, axis) {				\
175636d4463SLucas Stankus 	.type = IIO_ACCEL,						\
176636d4463SLucas Stankus 	.address = index,						\
177636d4463SLucas Stankus 	.modified = 1,							\
178636d4463SLucas Stankus 	.channel2 = IIO_MOD_##axis,					\
179636d4463SLucas Stankus 	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
180636d4463SLucas Stankus 			      BIT(IIO_CHAN_INFO_CALIBBIAS),		\
181636d4463SLucas Stankus 	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |		\
182636d4463SLucas Stankus 				    BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
183636d4463SLucas Stankus 	.info_mask_shared_by_type_available =				\
184636d4463SLucas Stankus 		BIT(IIO_CHAN_INFO_SAMP_FREQ),				\
185636d4463SLucas Stankus 	.scan_type = {							\
186636d4463SLucas Stankus 		.realbits = 13,						\
187636d4463SLucas Stankus 	},								\
188636d4463SLucas Stankus }
189636d4463SLucas Stankus 
190636d4463SLucas Stankus static const struct iio_chan_spec adxl313_channels[] = {
191636d4463SLucas Stankus 	ADXL313_ACCEL_CHANNEL(0, X),
192636d4463SLucas Stankus 	ADXL313_ACCEL_CHANNEL(1, Y),
193636d4463SLucas Stankus 	ADXL313_ACCEL_CHANNEL(2, Z),
194636d4463SLucas Stankus };
195636d4463SLucas Stankus 
adxl313_set_odr(struct adxl313_data * data,unsigned int freq1,unsigned int freq2)196636d4463SLucas Stankus static int adxl313_set_odr(struct adxl313_data *data,
197636d4463SLucas Stankus 			   unsigned int freq1, unsigned int freq2)
198636d4463SLucas Stankus {
199636d4463SLucas Stankus 	unsigned int i;
200636d4463SLucas Stankus 
201636d4463SLucas Stankus 	for (i = 0; i < ARRAY_SIZE(adxl313_odr_freqs); i++) {
202636d4463SLucas Stankus 		if (adxl313_odr_freqs[i][0] == freq1 &&
203636d4463SLucas Stankus 		    adxl313_odr_freqs[i][1] == freq2)
204636d4463SLucas Stankus 			break;
205636d4463SLucas Stankus 	}
206636d4463SLucas Stankus 
207636d4463SLucas Stankus 	if (i == ARRAY_SIZE(adxl313_odr_freqs))
208636d4463SLucas Stankus 		return -EINVAL;
209636d4463SLucas Stankus 
210636d4463SLucas Stankus 	return regmap_update_bits(data->regmap, ADXL313_REG_BW_RATE,
211636d4463SLucas Stankus 				  ADXL313_RATE_MSK,
212636d4463SLucas Stankus 				  FIELD_PREP(ADXL313_RATE_MSK, ADXL313_RATE_BASE + i));
213636d4463SLucas Stankus }
214636d4463SLucas Stankus 
adxl313_read_axis(struct adxl313_data * data,struct iio_chan_spec const * chan)215636d4463SLucas Stankus static int adxl313_read_axis(struct adxl313_data *data,
216636d4463SLucas Stankus 			     struct iio_chan_spec const *chan)
217636d4463SLucas Stankus {
218636d4463SLucas Stankus 	int ret;
219636d4463SLucas Stankus 
220636d4463SLucas Stankus 	mutex_lock(&data->lock);
221636d4463SLucas Stankus 
222636d4463SLucas Stankus 	ret = regmap_bulk_read(data->regmap,
223636d4463SLucas Stankus 			       ADXL313_REG_DATA_AXIS(chan->address),
224636d4463SLucas Stankus 			       &data->transf_buf, sizeof(data->transf_buf));
225636d4463SLucas Stankus 	if (ret)
226636d4463SLucas Stankus 		goto unlock_ret;
227636d4463SLucas Stankus 
228636d4463SLucas Stankus 	ret = le16_to_cpu(data->transf_buf);
229636d4463SLucas Stankus 
230636d4463SLucas Stankus unlock_ret:
231636d4463SLucas Stankus 	mutex_unlock(&data->lock);
232636d4463SLucas Stankus 	return ret;
233636d4463SLucas Stankus }
234636d4463SLucas Stankus 
adxl313_read_freq_avail(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,const int ** vals,int * type,int * length,long mask)235636d4463SLucas Stankus static int adxl313_read_freq_avail(struct iio_dev *indio_dev,
236636d4463SLucas Stankus 				   struct iio_chan_spec const *chan,
237636d4463SLucas Stankus 				   const int **vals, int *type, int *length,
238636d4463SLucas Stankus 				   long mask)
239636d4463SLucas Stankus {
240636d4463SLucas Stankus 	switch (mask) {
241636d4463SLucas Stankus 	case IIO_CHAN_INFO_SAMP_FREQ:
242636d4463SLucas Stankus 		*vals = (const int *)adxl313_odr_freqs;
243636d4463SLucas Stankus 		*length = ARRAY_SIZE(adxl313_odr_freqs) * 2;
244636d4463SLucas Stankus 		*type = IIO_VAL_INT_PLUS_MICRO;
245636d4463SLucas Stankus 		return IIO_AVAIL_LIST;
246636d4463SLucas Stankus 	default:
247636d4463SLucas Stankus 		return -EINVAL;
248636d4463SLucas Stankus 	}
249636d4463SLucas Stankus }
250636d4463SLucas Stankus 
adxl313_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)251636d4463SLucas Stankus static int adxl313_read_raw(struct iio_dev *indio_dev,
252636d4463SLucas Stankus 			    struct iio_chan_spec const *chan,
253636d4463SLucas Stankus 			    int *val, int *val2, long mask)
254636d4463SLucas Stankus {
255636d4463SLucas Stankus 	struct adxl313_data *data = iio_priv(indio_dev);
256636d4463SLucas Stankus 	unsigned int regval;
257636d4463SLucas Stankus 	int ret;
258636d4463SLucas Stankus 
259636d4463SLucas Stankus 	switch (mask) {
260636d4463SLucas Stankus 	case IIO_CHAN_INFO_RAW:
261636d4463SLucas Stankus 		ret = adxl313_read_axis(data, chan);
262636d4463SLucas Stankus 		if (ret < 0)
263636d4463SLucas Stankus 			return ret;
264636d4463SLucas Stankus 
265636d4463SLucas Stankus 		*val = sign_extend32(ret, chan->scan_type.realbits - 1);
266636d4463SLucas Stankus 		return IIO_VAL_INT;
267636d4463SLucas Stankus 	case IIO_CHAN_INFO_SCALE:
268636d4463SLucas Stankus 		*val = 0;
269*a7a1c60bSGeorge Mois 
270*a7a1c60bSGeorge Mois 		*val2 = data->chip_info->scale_factor;
271*a7a1c60bSGeorge Mois 
272636d4463SLucas Stankus 		return IIO_VAL_INT_PLUS_NANO;
273636d4463SLucas Stankus 	case IIO_CHAN_INFO_CALIBBIAS:
274636d4463SLucas Stankus 		ret = regmap_read(data->regmap,
275636d4463SLucas Stankus 				  ADXL313_REG_OFS_AXIS(chan->address), &regval);
276636d4463SLucas Stankus 		if (ret)
277636d4463SLucas Stankus 			return ret;
278636d4463SLucas Stankus 
279636d4463SLucas Stankus 		/*
280*a7a1c60bSGeorge Mois 		 * 8-bit resolution at minimum range, that is 4x accel data scale
281636d4463SLucas Stankus 		 * factor at full resolution
282636d4463SLucas Stankus 		 */
283636d4463SLucas Stankus 		*val = sign_extend32(regval, 7) * 4;
284636d4463SLucas Stankus 		return IIO_VAL_INT;
285636d4463SLucas Stankus 	case IIO_CHAN_INFO_SAMP_FREQ:
286636d4463SLucas Stankus 		ret = regmap_read(data->regmap, ADXL313_REG_BW_RATE, &regval);
287636d4463SLucas Stankus 		if (ret)
288636d4463SLucas Stankus 			return ret;
289636d4463SLucas Stankus 
290636d4463SLucas Stankus 		ret = FIELD_GET(ADXL313_RATE_MSK, regval) - ADXL313_RATE_BASE;
291636d4463SLucas Stankus 		*val = adxl313_odr_freqs[ret][0];
292636d4463SLucas Stankus 		*val2 = adxl313_odr_freqs[ret][1];
293636d4463SLucas Stankus 		return IIO_VAL_INT_PLUS_MICRO;
294636d4463SLucas Stankus 	default:
295636d4463SLucas Stankus 		return -EINVAL;
296636d4463SLucas Stankus 	}
297636d4463SLucas Stankus }
298636d4463SLucas Stankus 
adxl313_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)299636d4463SLucas Stankus static int adxl313_write_raw(struct iio_dev *indio_dev,
300636d4463SLucas Stankus 			     struct iio_chan_spec const *chan,
301636d4463SLucas Stankus 			     int val, int val2, long mask)
302636d4463SLucas Stankus {
303636d4463SLucas Stankus 	struct adxl313_data *data = iio_priv(indio_dev);
304636d4463SLucas Stankus 
305636d4463SLucas Stankus 	switch (mask) {
306636d4463SLucas Stankus 	case IIO_CHAN_INFO_CALIBBIAS:
307636d4463SLucas Stankus 		/*
308*a7a1c60bSGeorge Mois 		 * 8-bit resolution at minimum range, that is 4x accel data scale
309636d4463SLucas Stankus 		 * factor at full resolution
310636d4463SLucas Stankus 		 */
311636d4463SLucas Stankus 		if (clamp_val(val, -128 * 4, 127 * 4) != val)
312636d4463SLucas Stankus 			return -EINVAL;
313636d4463SLucas Stankus 
314636d4463SLucas Stankus 		return regmap_write(data->regmap,
315636d4463SLucas Stankus 				    ADXL313_REG_OFS_AXIS(chan->address),
316636d4463SLucas Stankus 				    val / 4);
317636d4463SLucas Stankus 	case IIO_CHAN_INFO_SAMP_FREQ:
318636d4463SLucas Stankus 		return adxl313_set_odr(data, val, val2);
319636d4463SLucas Stankus 	default:
320636d4463SLucas Stankus 		return -EINVAL;
321636d4463SLucas Stankus 	}
322636d4463SLucas Stankus }
323636d4463SLucas Stankus 
324636d4463SLucas Stankus static const struct iio_info adxl313_info = {
325636d4463SLucas Stankus 	.read_raw	= adxl313_read_raw,
326636d4463SLucas Stankus 	.write_raw	= adxl313_write_raw,
327636d4463SLucas Stankus 	.read_avail	= adxl313_read_freq_avail,
328636d4463SLucas Stankus };
329636d4463SLucas Stankus 
adxl313_setup(struct device * dev,struct adxl313_data * data,int (* setup)(struct device *,struct regmap *))330636d4463SLucas Stankus static int adxl313_setup(struct device *dev, struct adxl313_data *data,
331636d4463SLucas Stankus 			 int (*setup)(struct device *, struct regmap *))
332636d4463SLucas Stankus {
333636d4463SLucas Stankus 	int ret;
334636d4463SLucas Stankus 
335*a7a1c60bSGeorge Mois 	/*
336*a7a1c60bSGeorge Mois 	 * If sw reset available, ensures the device is in a consistent
337*a7a1c60bSGeorge Mois 	 * state after start up
338*a7a1c60bSGeorge Mois 	 */
339*a7a1c60bSGeorge Mois 	if (data->chip_info->soft_reset) {
340636d4463SLucas Stankus 		ret = regmap_write(data->regmap, ADXL313_REG_SOFT_RESET,
341636d4463SLucas Stankus 				   ADXL313_SOFT_RESET);
342636d4463SLucas Stankus 		if (ret)
343636d4463SLucas Stankus 			return ret;
344*a7a1c60bSGeorge Mois 	}
345636d4463SLucas Stankus 
346636d4463SLucas Stankus 	if (setup) {
347636d4463SLucas Stankus 		ret = setup(dev, data->regmap);
348636d4463SLucas Stankus 		if (ret)
349636d4463SLucas Stankus 			return ret;
350636d4463SLucas Stankus 	}
351636d4463SLucas Stankus 
352*a7a1c60bSGeorge Mois 	ret = data->chip_info->check_id(dev, data);
353636d4463SLucas Stankus 	if (ret)
354636d4463SLucas Stankus 		return ret;
355636d4463SLucas Stankus 
356*a7a1c60bSGeorge Mois 	/* Sets the range to maximum, full resolution, if applicable */
357*a7a1c60bSGeorge Mois 	if (data->chip_info->variable_range) {
358636d4463SLucas Stankus 		ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT,
359636d4463SLucas Stankus 					 ADXL313_RANGE_MSK,
360*a7a1c60bSGeorge Mois 					 FIELD_PREP(ADXL313_RANGE_MSK, ADXL313_RANGE_MAX));
361636d4463SLucas Stankus 		if (ret)
362636d4463SLucas Stankus 			return ret;
363636d4463SLucas Stankus 
364636d4463SLucas Stankus 		/* Enables full resolution */
365636d4463SLucas Stankus 		ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT,
366636d4463SLucas Stankus 					 ADXL313_FULL_RES, ADXL313_FULL_RES);
367636d4463SLucas Stankus 		if (ret)
368636d4463SLucas Stankus 			return ret;
369*a7a1c60bSGeorge Mois 	}
370636d4463SLucas Stankus 
371636d4463SLucas Stankus 	/* Enables measurement mode */
372636d4463SLucas Stankus 	return regmap_update_bits(data->regmap, ADXL313_REG_POWER_CTL,
373636d4463SLucas Stankus 				  ADXL313_POWER_CTL_MSK,
374636d4463SLucas Stankus 				  ADXL313_MEASUREMENT_MODE);
375636d4463SLucas Stankus }
376636d4463SLucas Stankus 
377636d4463SLucas Stankus /**
378636d4463SLucas Stankus  * adxl313_core_probe() - probe and setup for adxl313 accelerometer
379636d4463SLucas Stankus  * @dev:	Driver model representation of the device
380636d4463SLucas Stankus  * @regmap:	Register map of the device
381*a7a1c60bSGeorge Mois  * @chip_info:	Structure containing device specific data
382636d4463SLucas Stankus  * @setup:	Setup routine to be executed right before the standard device
383636d4463SLucas Stankus  *		setup, can also be set to NULL if not required
384636d4463SLucas Stankus  *
385636d4463SLucas Stankus  * Return: 0 on success, negative errno on error cases
386636d4463SLucas Stankus  */
adxl313_core_probe(struct device * dev,struct regmap * regmap,const struct adxl313_chip_info * chip_info,int (* setup)(struct device *,struct regmap *))387636d4463SLucas Stankus int adxl313_core_probe(struct device *dev,
388636d4463SLucas Stankus 		       struct regmap *regmap,
389*a7a1c60bSGeorge Mois 		       const struct adxl313_chip_info *chip_info,
390636d4463SLucas Stankus 		       int (*setup)(struct device *, struct regmap *))
391636d4463SLucas Stankus {
392636d4463SLucas Stankus 	struct adxl313_data *data;
393636d4463SLucas Stankus 	struct iio_dev *indio_dev;
394636d4463SLucas Stankus 	int ret;
395636d4463SLucas Stankus 
396636d4463SLucas Stankus 	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
397636d4463SLucas Stankus 	if (!indio_dev)
398636d4463SLucas Stankus 		return -ENOMEM;
399636d4463SLucas Stankus 
400636d4463SLucas Stankus 	data = iio_priv(indio_dev);
401636d4463SLucas Stankus 	data->regmap = regmap;
402*a7a1c60bSGeorge Mois 	data->chip_info = chip_info;
403*a7a1c60bSGeorge Mois 
404636d4463SLucas Stankus 	mutex_init(&data->lock);
405636d4463SLucas Stankus 
406*a7a1c60bSGeorge Mois 	indio_dev->name = chip_info->name;
407636d4463SLucas Stankus 	indio_dev->info = &adxl313_info;
408636d4463SLucas Stankus 	indio_dev->modes = INDIO_DIRECT_MODE;
409636d4463SLucas Stankus 	indio_dev->channels = adxl313_channels;
410636d4463SLucas Stankus 	indio_dev->num_channels = ARRAY_SIZE(adxl313_channels);
411636d4463SLucas Stankus 
412636d4463SLucas Stankus 	ret = adxl313_setup(dev, data, setup);
413636d4463SLucas Stankus 	if (ret) {
414636d4463SLucas Stankus 		dev_err(dev, "ADXL313 setup failed\n");
415636d4463SLucas Stankus 		return ret;
416636d4463SLucas Stankus 	}
417636d4463SLucas Stankus 
418636d4463SLucas Stankus 	return devm_iio_device_register(dev, indio_dev);
419636d4463SLucas Stankus }
420fa4df5a9SJonathan Cameron EXPORT_SYMBOL_NS_GPL(adxl313_core_probe, IIO_ADXL313);
421636d4463SLucas Stankus 
422636d4463SLucas Stankus MODULE_AUTHOR("Lucas Stankus <lucas.p.stankus@gmail.com>");
423636d4463SLucas Stankus MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer core driver");
424636d4463SLucas Stankus MODULE_LICENSE("GPL v2");
425