Commit | Line | Data |
---|---|---|
c6236075 | 1 | /* |
5170512c | 2 | * ADXL345 3-Axis Digital Accelerometer IIO core driver |
c6236075 ERR |
3 | * |
4 | * Copyright (c) 2017 Eva Rachel Retuya <eraretuya@gmail.com> | |
5 | * | |
6 | * This file is subject to the terms and conditions of version 2 of | |
7 | * the GNU General Public License. See the file COPYING in the main | |
8 | * directory of this archive for more details. | |
4b5de1fa AM |
9 | * |
10 | * Datasheet: http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf | |
c6236075 ERR |
11 | */ |
12 | ||
c6236075 | 13 | #include <linux/module.h> |
31fd2c70 | 14 | #include <linux/regmap.h> |
c6236075 ERR |
15 | |
16 | #include <linux/iio/iio.h> | |
382fa581 | 17 | #include <linux/iio/sysfs.h> |
c6236075 | 18 | |
5170512c ERR |
19 | #include "adxl345.h" |
20 | ||
c6236075 | 21 | #define ADXL345_REG_DEVID 0x00 |
732238e2 AM |
22 | #define ADXL345_REG_OFSX 0x1e |
23 | #define ADXL345_REG_OFSY 0x1f | |
24 | #define ADXL345_REG_OFSZ 0x20 | |
25 | #define ADXL345_REG_OFS_AXIS(index) (ADXL345_REG_OFSX + (index)) | |
382fa581 | 26 | #define ADXL345_REG_BW_RATE 0x2C |
c6236075 ERR |
27 | #define ADXL345_REG_POWER_CTL 0x2D |
28 | #define ADXL345_REG_DATA_FORMAT 0x31 | |
29 | #define ADXL345_REG_DATAX0 0x32 | |
30 | #define ADXL345_REG_DATAY0 0x34 | |
31 | #define ADXL345_REG_DATAZ0 0x36 | |
9048f1f1 AM |
32 | #define ADXL345_REG_DATA_AXIS(index) \ |
33 | (ADXL345_REG_DATAX0 + (index) * sizeof(__le16)) | |
c6236075 | 34 | |
382fa581 AM |
35 | #define ADXL345_BW_RATE GENMASK(3, 0) |
36 | #define ADXL345_BASE_RATE_NANO_HZ 97656250LL | |
37 | #define NHZ_PER_HZ 1000000000LL | |
38 | ||
c6236075 ERR |
39 | #define ADXL345_POWER_CTL_MEASURE BIT(3) |
40 | #define ADXL345_POWER_CTL_STANDBY 0x00 | |
41 | ||
42 | #define ADXL345_DATA_FORMAT_FULL_RES BIT(3) /* Up to 13-bits resolution */ | |
43 | #define ADXL345_DATA_FORMAT_2G 0 | |
44 | #define ADXL345_DATA_FORMAT_4G 1 | |
45 | #define ADXL345_DATA_FORMAT_8G 2 | |
46 | #define ADXL345_DATA_FORMAT_16G 3 | |
47 | ||
48 | #define ADXL345_DEVID 0xE5 | |
49 | ||
50 | /* | |
51 | * In full-resolution mode, scale factor is maintained at ~4 mg/LSB | |
52 | * in all g ranges. | |
53 | * | |
54 | * At +/- 16g with 13-bit resolution, scale is computed as: | |
55 | * (16 + 16) * 9.81 / (2^13 - 1) = 0.0383 | |
56 | */ | |
57 | static const int adxl345_uscale = 38300; | |
58 | ||
ef89f4b9 LPC |
59 | /* |
60 | * The Datasheet lists a resolution of Resolution is ~49 mg per LSB. That's | |
61 | * ~480mm/s**2 per LSB. | |
62 | */ | |
63 | static const int adxl375_uscale = 480000; | |
64 | ||
c6236075 | 65 | struct adxl345_data { |
31fd2c70 | 66 | struct regmap *regmap; |
c6236075 | 67 | u8 data_range; |
ef89f4b9 | 68 | enum adxl345_device_type type; |
c6236075 ERR |
69 | }; |
70 | ||
9048f1f1 | 71 | #define ADXL345_CHANNEL(index, axis) { \ |
c6236075 ERR |
72 | .type = IIO_ACCEL, \ |
73 | .modified = 1, \ | |
74 | .channel2 = IIO_MOD_##axis, \ | |
9048f1f1 | 75 | .address = index, \ |
732238e2 AM |
76 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ |
77 | BIT(IIO_CHAN_INFO_CALIBBIAS), \ | |
382fa581 AM |
78 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ |
79 | BIT(IIO_CHAN_INFO_SAMP_FREQ), \ | |
c6236075 ERR |
80 | } |
81 | ||
82 | static const struct iio_chan_spec adxl345_channels[] = { | |
9048f1f1 AM |
83 | ADXL345_CHANNEL(0, X), |
84 | ADXL345_CHANNEL(1, Y), | |
85 | ADXL345_CHANNEL(2, Z), | |
c6236075 ERR |
86 | }; |
87 | ||
88 | static int adxl345_read_raw(struct iio_dev *indio_dev, | |
89 | struct iio_chan_spec const *chan, | |
90 | int *val, int *val2, long mask) | |
91 | { | |
92 | struct adxl345_data *data = iio_priv(indio_dev); | |
9048f1f1 | 93 | __le16 accel; |
382fa581 | 94 | long long samp_freq_nhz; |
732238e2 | 95 | unsigned int regval; |
c6236075 ERR |
96 | int ret; |
97 | ||
98 | switch (mask) { | |
99 | case IIO_CHAN_INFO_RAW: | |
100 | /* | |
101 | * Data is stored in adjacent registers: | |
102 | * ADXL345_REG_DATA(X0/Y0/Z0) contain the least significant byte | |
103 | * and ADXL345_REG_DATA(X0/Y0/Z0) + 1 the most significant byte | |
104 | */ | |
9048f1f1 AM |
105 | ret = regmap_bulk_read(data->regmap, |
106 | ADXL345_REG_DATA_AXIS(chan->address), | |
107 | &accel, sizeof(accel)); | |
c6236075 ERR |
108 | if (ret < 0) |
109 | return ret; | |
110 | ||
9048f1f1 | 111 | *val = sign_extend32(le16_to_cpu(accel), 12); |
c6236075 ERR |
112 | return IIO_VAL_INT; |
113 | case IIO_CHAN_INFO_SCALE: | |
114 | *val = 0; | |
ef89f4b9 LPC |
115 | switch (data->type) { |
116 | case ADXL345: | |
117 | *val2 = adxl345_uscale; | |
118 | break; | |
119 | case ADXL375: | |
120 | *val2 = adxl375_uscale; | |
121 | break; | |
122 | } | |
c6236075 ERR |
123 | |
124 | return IIO_VAL_INT_PLUS_MICRO; | |
732238e2 AM |
125 | case IIO_CHAN_INFO_CALIBBIAS: |
126 | ret = regmap_read(data->regmap, | |
127 | ADXL345_REG_OFS_AXIS(chan->address), ®val); | |
128 | if (ret < 0) | |
129 | return ret; | |
130 | /* | |
131 | * 8-bit resolution at +/- 2g, that is 4x accel data scale | |
132 | * factor | |
133 | */ | |
134 | *val = sign_extend32(regval, 7) * 4; | |
135 | ||
136 | return IIO_VAL_INT; | |
382fa581 AM |
137 | case IIO_CHAN_INFO_SAMP_FREQ: |
138 | ret = regmap_read(data->regmap, ADXL345_REG_BW_RATE, ®val); | |
139 | if (ret < 0) | |
140 | return ret; | |
141 | ||
142 | samp_freq_nhz = ADXL345_BASE_RATE_NANO_HZ << | |
143 | (regval & ADXL345_BW_RATE); | |
144 | *val = div_s64_rem(samp_freq_nhz, NHZ_PER_HZ, val2); | |
145 | ||
146 | return IIO_VAL_INT_PLUS_NANO; | |
732238e2 AM |
147 | } |
148 | ||
149 | return -EINVAL; | |
150 | } | |
151 | ||
152 | static int adxl345_write_raw(struct iio_dev *indio_dev, | |
153 | struct iio_chan_spec const *chan, | |
154 | int val, int val2, long mask) | |
155 | { | |
156 | struct adxl345_data *data = iio_priv(indio_dev); | |
382fa581 | 157 | s64 n; |
732238e2 AM |
158 | |
159 | switch (mask) { | |
160 | case IIO_CHAN_INFO_CALIBBIAS: | |
161 | /* | |
162 | * 8-bit resolution at +/- 2g, that is 4x accel data scale | |
163 | * factor | |
164 | */ | |
165 | return regmap_write(data->regmap, | |
166 | ADXL345_REG_OFS_AXIS(chan->address), | |
167 | val / 4); | |
382fa581 AM |
168 | case IIO_CHAN_INFO_SAMP_FREQ: |
169 | n = div_s64(val * NHZ_PER_HZ + val2, ADXL345_BASE_RATE_NANO_HZ); | |
170 | ||
171 | return regmap_update_bits(data->regmap, ADXL345_REG_BW_RATE, | |
172 | ADXL345_BW_RATE, | |
173 | clamp_val(ilog2(n), 0, | |
174 | ADXL345_BW_RATE)); | |
c6236075 ERR |
175 | } |
176 | ||
177 | return -EINVAL; | |
178 | } | |
179 | ||
382fa581 AM |
180 | static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev, |
181 | struct iio_chan_spec const *chan, | |
182 | long mask) | |
183 | { | |
184 | switch (mask) { | |
185 | case IIO_CHAN_INFO_CALIBBIAS: | |
186 | return IIO_VAL_INT; | |
187 | case IIO_CHAN_INFO_SAMP_FREQ: | |
188 | return IIO_VAL_INT_PLUS_NANO; | |
189 | default: | |
190 | return -EINVAL; | |
191 | } | |
192 | } | |
193 | ||
194 | static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( | |
195 | "0.09765625 0.1953125 0.390625 0.78125 1.5625 3.125 6.25 12.5 25 50 100 200 400 800 1600 3200" | |
196 | ); | |
197 | ||
198 | static struct attribute *adxl345_attrs[] = { | |
199 | &iio_const_attr_sampling_frequency_available.dev_attr.attr, | |
200 | NULL, | |
201 | }; | |
202 | ||
203 | static const struct attribute_group adxl345_attrs_group = { | |
204 | .attrs = adxl345_attrs, | |
205 | }; | |
206 | ||
c6236075 | 207 | static const struct iio_info adxl345_info = { |
382fa581 | 208 | .attrs = &adxl345_attrs_group, |
c6236075 | 209 | .read_raw = adxl345_read_raw, |
732238e2 | 210 | .write_raw = adxl345_write_raw, |
382fa581 | 211 | .write_raw_get_fmt = adxl345_write_raw_get_fmt, |
c6236075 ERR |
212 | }; |
213 | ||
5170512c | 214 | int adxl345_core_probe(struct device *dev, struct regmap *regmap, |
ef89f4b9 | 215 | enum adxl345_device_type type, const char *name) |
c6236075 ERR |
216 | { |
217 | struct adxl345_data *data; | |
218 | struct iio_dev *indio_dev; | |
31fd2c70 | 219 | u32 regval; |
c6236075 ERR |
220 | int ret; |
221 | ||
31fd2c70 | 222 | ret = regmap_read(regmap, ADXL345_REG_DEVID, ®val); |
c6236075 | 223 | if (ret < 0) { |
31fd2c70 | 224 | dev_err(dev, "Error reading device ID: %d\n", ret); |
c6236075 ERR |
225 | return ret; |
226 | } | |
227 | ||
31fd2c70 ERR |
228 | if (regval != ADXL345_DEVID) { |
229 | dev_err(dev, "Invalid device ID: %x, expected %x\n", | |
230 | regval, ADXL345_DEVID); | |
c6236075 ERR |
231 | return -ENODEV; |
232 | } | |
233 | ||
31fd2c70 | 234 | indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); |
c6236075 ERR |
235 | if (!indio_dev) |
236 | return -ENOMEM; | |
237 | ||
238 | data = iio_priv(indio_dev); | |
31fd2c70 ERR |
239 | dev_set_drvdata(dev, indio_dev); |
240 | data->regmap = regmap; | |
ef89f4b9 | 241 | data->type = type; |
c6236075 ERR |
242 | /* Enable full-resolution mode */ |
243 | data->data_range = ADXL345_DATA_FORMAT_FULL_RES; | |
244 | ||
31fd2c70 ERR |
245 | ret = regmap_write(data->regmap, ADXL345_REG_DATA_FORMAT, |
246 | data->data_range); | |
c6236075 | 247 | if (ret < 0) { |
31fd2c70 | 248 | dev_err(dev, "Failed to set data range: %d\n", ret); |
c6236075 ERR |
249 | return ret; |
250 | } | |
251 | ||
31fd2c70 | 252 | indio_dev->dev.parent = dev; |
5170512c | 253 | indio_dev->name = name; |
c6236075 ERR |
254 | indio_dev->info = &adxl345_info; |
255 | indio_dev->modes = INDIO_DIRECT_MODE; | |
256 | indio_dev->channels = adxl345_channels; | |
257 | indio_dev->num_channels = ARRAY_SIZE(adxl345_channels); | |
258 | ||
259 | /* Enable measurement mode */ | |
31fd2c70 ERR |
260 | ret = regmap_write(data->regmap, ADXL345_REG_POWER_CTL, |
261 | ADXL345_POWER_CTL_MEASURE); | |
c6236075 | 262 | if (ret < 0) { |
31fd2c70 | 263 | dev_err(dev, "Failed to enable measurement mode: %d\n", ret); |
c6236075 ERR |
264 | return ret; |
265 | } | |
266 | ||
267 | ret = iio_device_register(indio_dev); | |
268 | if (ret < 0) { | |
31fd2c70 ERR |
269 | dev_err(dev, "iio_device_register failed: %d\n", ret); |
270 | regmap_write(data->regmap, ADXL345_REG_POWER_CTL, | |
271 | ADXL345_POWER_CTL_STANDBY); | |
c6236075 ERR |
272 | } |
273 | ||
274 | return ret; | |
275 | } | |
5170512c | 276 | EXPORT_SYMBOL_GPL(adxl345_core_probe); |
c6236075 | 277 | |
5170512c | 278 | int adxl345_core_remove(struct device *dev) |
c6236075 | 279 | { |
5170512c | 280 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
c6236075 ERR |
281 | struct adxl345_data *data = iio_priv(indio_dev); |
282 | ||
283 | iio_device_unregister(indio_dev); | |
284 | ||
31fd2c70 ERR |
285 | return regmap_write(data->regmap, ADXL345_REG_POWER_CTL, |
286 | ADXL345_POWER_CTL_STANDBY); | |
c6236075 | 287 | } |
5170512c | 288 | EXPORT_SYMBOL_GPL(adxl345_core_remove); |
c6236075 ERR |
289 | |
290 | MODULE_AUTHOR("Eva Rachel Retuya <eraretuya@gmail.com>"); | |
5170512c | 291 | MODULE_DESCRIPTION("ADXL345 3-Axis Digital Accelerometer core driver"); |
c6236075 | 292 | MODULE_LICENSE("GPL v2"); |