1 // SPDX-License-Identifier: GPL-2.0-only
3 * ADXL345 3-Axis Digital Accelerometer IIO core driver
5 * Copyright (c) 2017 Eva Rachel Retuya <eraretuya@gmail.com>
7 * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf
10 #include <linux/module.h>
11 #include <linux/property.h>
12 #include <linux/regmap.h>
13 #include <linux/units.h>
15 #include <linux/iio/iio.h>
16 #include <linux/iio/sysfs.h>
21 const struct adxl345_chip_info
*info
;
22 struct regmap
*regmap
;
25 #define ADXL345_CHANNEL(index, axis) { \
28 .channel2 = IIO_MOD_##axis, \
30 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
31 BIT(IIO_CHAN_INFO_CALIBBIAS), \
32 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
33 BIT(IIO_CHAN_INFO_SAMP_FREQ), \
36 static const struct iio_chan_spec adxl345_channels
[] = {
37 ADXL345_CHANNEL(0, X
),
38 ADXL345_CHANNEL(1, Y
),
39 ADXL345_CHANNEL(2, Z
),
42 static int adxl345_read_raw(struct iio_dev
*indio_dev
,
43 struct iio_chan_spec
const *chan
,
44 int *val
, int *val2
, long mask
)
46 struct adxl345_data
*data
= iio_priv(indio_dev
);
48 long long samp_freq_nhz
;
53 case IIO_CHAN_INFO_RAW
:
55 * Data is stored in adjacent registers:
56 * ADXL345_REG_DATA(X0/Y0/Z0) contain the least significant byte
57 * and ADXL345_REG_DATA(X0/Y0/Z0) + 1 the most significant byte
59 ret
= regmap_bulk_read(data
->regmap
,
60 ADXL345_REG_DATA_AXIS(chan
->address
),
61 &accel
, sizeof(accel
));
65 *val
= sign_extend32(le16_to_cpu(accel
), 12);
67 case IIO_CHAN_INFO_SCALE
:
69 *val2
= data
->info
->uscale
;
70 return IIO_VAL_INT_PLUS_MICRO
;
71 case IIO_CHAN_INFO_CALIBBIAS
:
72 ret
= regmap_read(data
->regmap
,
73 ADXL345_REG_OFS_AXIS(chan
->address
), ®val
);
77 * 8-bit resolution at +/- 2g, that is 4x accel data scale
80 *val
= sign_extend32(regval
, 7) * 4;
83 case IIO_CHAN_INFO_SAMP_FREQ
:
84 ret
= regmap_read(data
->regmap
, ADXL345_REG_BW_RATE
, ®val
);
88 samp_freq_nhz
= ADXL345_BASE_RATE_NANO_HZ
<<
89 (regval
& ADXL345_BW_RATE
);
90 *val
= div_s64_rem(samp_freq_nhz
, NANOHZ_PER_HZ
, val2
);
92 return IIO_VAL_INT_PLUS_NANO
;
98 static int adxl345_write_raw(struct iio_dev
*indio_dev
,
99 struct iio_chan_spec
const *chan
,
100 int val
, int val2
, long mask
)
102 struct adxl345_data
*data
= iio_priv(indio_dev
);
106 case IIO_CHAN_INFO_CALIBBIAS
:
108 * 8-bit resolution at +/- 2g, that is 4x accel data scale
111 return regmap_write(data
->regmap
,
112 ADXL345_REG_OFS_AXIS(chan
->address
),
114 case IIO_CHAN_INFO_SAMP_FREQ
:
115 n
= div_s64(val
* NANOHZ_PER_HZ
+ val2
,
116 ADXL345_BASE_RATE_NANO_HZ
);
118 return regmap_update_bits(data
->regmap
, ADXL345_REG_BW_RATE
,
120 clamp_val(ilog2(n
), 0,
127 static int adxl345_write_raw_get_fmt(struct iio_dev
*indio_dev
,
128 struct iio_chan_spec
const *chan
,
132 case IIO_CHAN_INFO_CALIBBIAS
:
134 case IIO_CHAN_INFO_SAMP_FREQ
:
135 return IIO_VAL_INT_PLUS_NANO
;
141 static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
142 "0.09765625 0.1953125 0.390625 0.78125 1.5625 3.125 6.25 12.5 25 50 100 200 400 800 1600 3200"
145 static struct attribute
*adxl345_attrs
[] = {
146 &iio_const_attr_sampling_frequency_available
.dev_attr
.attr
,
150 static const struct attribute_group adxl345_attrs_group
= {
151 .attrs
= adxl345_attrs
,
154 static const struct iio_info adxl345_info
= {
155 .attrs
= &adxl345_attrs_group
,
156 .read_raw
= adxl345_read_raw
,
157 .write_raw
= adxl345_write_raw
,
158 .write_raw_get_fmt
= adxl345_write_raw_get_fmt
,
161 static int adxl345_powerup(void *regmap
)
163 return regmap_write(regmap
, ADXL345_REG_POWER_CTL
, ADXL345_POWER_CTL_MEASURE
);
166 static void adxl345_powerdown(void *regmap
)
168 regmap_write(regmap
, ADXL345_REG_POWER_CTL
, ADXL345_POWER_CTL_STANDBY
);
172 * adxl345_core_probe() - probe and setup for the adxl345 accelerometer,
173 * also covers the adlx375 accelerometer
174 * @dev: Driver model representation of the device
175 * @regmap: Regmap instance for the device
176 * @setup: Setup routine to be executed right before the standard device
179 * Return: 0 on success, negative errno on error
181 int adxl345_core_probe(struct device
*dev
, struct regmap
*regmap
,
182 int (*setup
)(struct device
*, struct regmap
*))
184 struct adxl345_data
*data
;
185 struct iio_dev
*indio_dev
;
187 unsigned int data_format_mask
= (ADXL345_DATA_FORMAT_RANGE
|
188 ADXL345_DATA_FORMAT_JUSTIFY
|
189 ADXL345_DATA_FORMAT_FULL_RES
|
190 ADXL345_DATA_FORMAT_SELF_TEST
);
193 indio_dev
= devm_iio_device_alloc(dev
, sizeof(*data
));
197 data
= iio_priv(indio_dev
);
198 data
->regmap
= regmap
;
199 data
->info
= device_get_match_data(dev
);
203 indio_dev
->name
= data
->info
->name
;
204 indio_dev
->info
= &adxl345_info
;
205 indio_dev
->modes
= INDIO_DIRECT_MODE
;
206 indio_dev
->channels
= adxl345_channels
;
207 indio_dev
->num_channels
= ARRAY_SIZE(adxl345_channels
);
210 /* Perform optional initial bus specific configuration */
211 ret
= setup(dev
, data
->regmap
);
215 /* Enable full-resolution mode */
216 ret
= regmap_update_bits(data
->regmap
, ADXL345_REG_DATA_FORMAT
,
218 ADXL345_DATA_FORMAT_FULL_RES
);
220 return dev_err_probe(dev
, ret
,
221 "Failed to set data range\n");
224 /* Enable full-resolution mode (init all data_format bits) */
225 ret
= regmap_write(data
->regmap
, ADXL345_REG_DATA_FORMAT
,
226 ADXL345_DATA_FORMAT_FULL_RES
);
228 return dev_err_probe(dev
, ret
,
229 "Failed to set data range\n");
232 ret
= regmap_read(data
->regmap
, ADXL345_REG_DEVID
, ®val
);
234 return dev_err_probe(dev
, ret
, "Error reading device ID\n");
236 if (regval
!= ADXL345_DEVID
)
237 return dev_err_probe(dev
, -ENODEV
, "Invalid device ID: %x, expected %x\n",
238 regval
, ADXL345_DEVID
);
240 /* Enable measurement mode */
241 ret
= adxl345_powerup(data
->regmap
);
243 return dev_err_probe(dev
, ret
, "Failed to enable measurement mode\n");
245 ret
= devm_add_action_or_reset(dev
, adxl345_powerdown
, data
->regmap
);
249 return devm_iio_device_register(dev
, indio_dev
);
251 EXPORT_SYMBOL_NS_GPL(adxl345_core_probe
, IIO_ADXL345
);
253 MODULE_AUTHOR("Eva Rachel Retuya <eraretuya@gmail.com>");
254 MODULE_DESCRIPTION("ADXL345 3-Axis Digital Accelerometer core driver");
255 MODULE_LICENSE("GPL v2");