aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTeodora Baluta <teodora.baluta@intel.com>2015-08-20 10:37:31 -0400
committerJonathan Cameron <jic23@kernel.org>2015-08-31 12:32:05 -0400
commit077377fc4f74899c58e946e47352216412d0bb3a (patch)
treea00dcc7a9ff50941275c55017da1a2fae7eae47a
parentb1d125cc6236399258025b0c5646cafa2b45e043 (diff)
iio: accel: add support for mxc4005 accelerometer
This patch adds support for Memsic MXC4005XC 3-axis accelerometer. The current implementation is a minimal one as it adds raw readings for the three axes and setting scale from userspace. Signed-off-by: Teodora Baluta <teodora.baluta@intel.com> Signed-off-by: Jonathan Cameron <jic23@kernel.org>
-rw-r--r--drivers/iio/accel/Kconfig11
-rw-r--r--drivers/iio/accel/Makefile2
-rw-r--r--drivers/iio/accel/mxc4005.c354
3 files changed, 367 insertions, 0 deletions
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index a59047d7657e..69302bed2860 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -137,6 +137,17 @@ config MMA9553
137 To compile this driver as a module, choose M here: the module 137 To compile this driver as a module, choose M here: the module
138 will be called mma9553. 138 will be called mma9553.
139 139
140config MXC4005
141 tristate "Memsic MXC4005XC 3-Axis Accelerometer Driver"
142 depends on I2C
143 select REGMAP_I2C
144 help
145 Say yes here to build support for the Memsic MXC4005XC 3-axis
146 accelerometer.
147
148 To compile this driver as a module, choose M. The module will be
149 called mxc4005.
150
140config STK8312 151config STK8312
141 tristate "Sensortek STK8312 3-Axis Accelerometer Driver" 152 tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
142 depends on I2C 153 depends on I2C
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index ebd2675b2a02..020dda0ae508 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -14,6 +14,8 @@ obj-$(CONFIG_MMA9551_CORE) += mma9551_core.o
14obj-$(CONFIG_MMA9551) += mma9551.o 14obj-$(CONFIG_MMA9551) += mma9551.o
15obj-$(CONFIG_MMA9553) += mma9553.o 15obj-$(CONFIG_MMA9553) += mma9553.o
16 16
17obj-$(CONFIG_MXC4005) += mxc4005.o
18
17obj-$(CONFIG_STK8312) += stk8312.o 19obj-$(CONFIG_STK8312) += stk8312.o
18obj-$(CONFIG_STK8BA50) += stk8ba50.o 20obj-$(CONFIG_STK8BA50) += stk8ba50.o
19 21
diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c
new file mode 100644
index 000000000000..e15c1bd26bd1
--- /dev/null
+++ b/drivers/iio/accel/mxc4005.c
@@ -0,0 +1,354 @@
1/*
2 * 3-axis accelerometer driver for MXC4005XC Memsic sensor
3 *
4 * Copyright (c) 2014, Intel Corporation.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 */
15
16#include <linux/module.h>
17#include <linux/i2c.h>
18#include <linux/iio/iio.h>
19#include <linux/acpi.h>
20#include <linux/regmap.h>
21#include <linux/iio/sysfs.h>
22
23#define MXC4005_DRV_NAME "mxc4005"
24#define MXC4005_REGMAP_NAME "mxc4005_regmap"
25
26#define MXC4005_REG_XOUT_UPPER 0x03
27#define MXC4005_REG_XOUT_LOWER 0x04
28#define MXC4005_REG_YOUT_UPPER 0x05
29#define MXC4005_REG_YOUT_LOWER 0x06
30#define MXC4005_REG_ZOUT_UPPER 0x07
31#define MXC4005_REG_ZOUT_LOWER 0x08
32
33#define MXC4005_REG_CONTROL 0x0D
34#define MXC4005_REG_CONTROL_MASK_FSR GENMASK(6, 5)
35#define MXC4005_CONTROL_FSR_SHIFT 5
36
37#define MXC4005_REG_DEVICE_ID 0x0E
38
39enum mxc4005_axis {
40 AXIS_X,
41 AXIS_Y,
42 AXIS_Z,
43};
44
45enum mxc4005_range {
46 MXC4005_RANGE_2G,
47 MXC4005_RANGE_4G,
48 MXC4005_RANGE_8G,
49};
50
51struct mxc4005_data {
52 struct device *dev;
53 struct mutex mutex;
54 struct regmap *regmap;
55};
56
57/*
58 * MXC4005 can operate in the following ranges:
59 * +/- 2G, 4G, 8G (the default +/-2G)
60 *
61 * (2 + 2) * 9.81 / (2^12 - 1) = 0.009582
62 * (4 + 4) * 9.81 / (2^12 - 1) = 0.019164
63 * (8 + 8) * 9.81 / (2^12 - 1) = 0.038329
64 */
65static const struct {
66 u8 range;
67 int scale;
68} mxc4005_scale_table[] = {
69 {MXC4005_RANGE_2G, 9582},
70 {MXC4005_RANGE_4G, 19164},
71 {MXC4005_RANGE_8G, 38329},
72};
73
74
75static IIO_CONST_ATTR(in_accel_scale_available, "0.009582 0.019164 0.038329");
76
77static struct attribute *mxc4005_attributes[] = {
78 &iio_const_attr_in_accel_scale_available.dev_attr.attr,
79 NULL,
80};
81
82static const struct attribute_group mxc4005_attrs_group = {
83 .attrs = mxc4005_attributes,
84};
85
86static bool mxc4005_is_readable_reg(struct device *dev, unsigned int reg)
87{
88 switch (reg) {
89 case MXC4005_REG_XOUT_UPPER:
90 case MXC4005_REG_XOUT_LOWER:
91 case MXC4005_REG_YOUT_UPPER:
92 case MXC4005_REG_YOUT_LOWER:
93 case MXC4005_REG_ZOUT_UPPER:
94 case MXC4005_REG_ZOUT_LOWER:
95 case MXC4005_REG_DEVICE_ID:
96 case MXC4005_REG_CONTROL:
97 return true;
98 default:
99 return false;
100 }
101}
102
103static bool mxc4005_is_writeable_reg(struct device *dev, unsigned int reg)
104{
105 switch (reg) {
106 case MXC4005_REG_CONTROL:
107 return true;
108 default:
109 return false;
110 }
111}
112
113static const struct regmap_config mxc4005_regmap_config = {
114 .name = MXC4005_REGMAP_NAME,
115
116 .reg_bits = 8,
117 .val_bits = 8,
118
119 .max_register = MXC4005_REG_DEVICE_ID,
120
121 .readable_reg = mxc4005_is_readable_reg,
122 .writeable_reg = mxc4005_is_writeable_reg,
123};
124
125static int mxc4005_read_axis(struct mxc4005_data *data,
126 unsigned int addr)
127{
128 __be16 reg;
129 int ret;
130
131 ret = regmap_bulk_read(data->regmap, addr, (u8 *) &reg, sizeof(reg));
132 if (ret < 0) {
133 dev_err(data->dev, "failed to read reg %02x\n", addr);
134 return ret;
135 }
136
137 return be16_to_cpu(reg);
138}
139
140static int mxc4005_read_scale(struct mxc4005_data *data)
141{
142 unsigned int reg;
143 int ret;
144 int i;
145
146 ret = regmap_read(data->regmap, MXC4005_REG_CONTROL, &reg);
147 if (ret < 0) {
148 dev_err(data->dev, "failed to read reg_control\n");
149 return ret;
150 }
151
152 i = reg >> MXC4005_CONTROL_FSR_SHIFT;
153
154 if (i < 0 || i >= ARRAY_SIZE(mxc4005_scale_table))
155 return -EINVAL;
156
157 return mxc4005_scale_table[i].scale;
158}
159
160static int mxc4005_set_scale(struct mxc4005_data *data, int val)
161{
162 unsigned int reg;
163 int i;
164 int ret;
165
166 for (i = 0; i < ARRAY_SIZE(mxc4005_scale_table); i++) {
167 if (mxc4005_scale_table[i].scale == val) {
168 reg = i << MXC4005_CONTROL_FSR_SHIFT;
169 ret = regmap_update_bits(data->regmap,
170 MXC4005_REG_CONTROL,
171 MXC4005_REG_CONTROL_MASK_FSR,
172 reg);
173 if (ret < 0)
174 dev_err(data->dev,
175 "failed to write reg_control\n");
176 return ret;
177 }
178 }
179
180 return -EINVAL;
181}
182
183static int mxc4005_read_raw(struct iio_dev *indio_dev,
184 struct iio_chan_spec const *chan,
185 int *val, int *val2, long mask)
186{
187 struct mxc4005_data *data = iio_priv(indio_dev);
188 int ret;
189
190 switch (mask) {
191 case IIO_CHAN_INFO_RAW:
192 switch (chan->type) {
193 case IIO_ACCEL:
194 if (iio_buffer_enabled(indio_dev))
195 return -EBUSY;
196
197 ret = mxc4005_read_axis(data, chan->address);
198 if (ret < 0)
199 return ret;
200 *val = sign_extend32(ret >> 4, 11);
201 return IIO_VAL_INT;
202 default:
203 return -EINVAL;
204 }
205 case IIO_CHAN_INFO_SCALE:
206 ret = mxc4005_read_scale(data);
207 if (ret < 0)
208 return ret;
209
210 *val = 0;
211 *val2 = ret;
212 return IIO_VAL_INT_PLUS_MICRO;
213 default:
214 return -EINVAL;
215 }
216}
217
218static int mxc4005_write_raw(struct iio_dev *indio_dev,
219 struct iio_chan_spec const *chan,
220 int val, int val2, long mask)
221{
222 struct mxc4005_data *data = iio_priv(indio_dev);
223
224 switch (mask) {
225 case IIO_CHAN_INFO_SCALE:
226 if (val != 0)
227 return -EINVAL;
228
229 return mxc4005_set_scale(data, val2);
230 default:
231 return -EINVAL;
232 }
233}
234
235static const struct iio_info mxc4005_info = {
236 .driver_module = THIS_MODULE,
237 .read_raw = mxc4005_read_raw,
238 .write_raw = mxc4005_write_raw,
239 .attrs = &mxc4005_attrs_group,
240};
241
242#define MXC4005_CHANNEL(_axis, _addr) { \
243 .type = IIO_ACCEL, \
244 .modified = 1, \
245 .channel2 = IIO_MOD_##_axis, \
246 .address = _addr, \
247 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
248 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
249}
250
251static const struct iio_chan_spec mxc4005_channels[] = {
252 MXC4005_CHANNEL(X, MXC4005_REG_XOUT_UPPER),
253 MXC4005_CHANNEL(Y, MXC4005_REG_YOUT_UPPER),
254 MXC4005_CHANNEL(Z, MXC4005_REG_ZOUT_UPPER),
255};
256
257static int mxc4005_chip_init(struct mxc4005_data *data)
258{
259 int ret;
260 unsigned int reg;
261
262 ret = regmap_read(data->regmap, MXC4005_REG_DEVICE_ID, &reg);
263 if (ret < 0) {
264 dev_err(data->dev, "failed to read chip id\n");
265 return ret;
266 }
267
268 dev_dbg(data->dev, "MXC4005 chip id %02x\n", reg);
269
270 return 0;
271}
272
273static int mxc4005_probe(struct i2c_client *client,
274 const struct i2c_device_id *id)
275{
276 struct mxc4005_data *data;
277 struct iio_dev *indio_dev;
278 struct regmap *regmap;
279 int ret;
280
281 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
282 if (!indio_dev)
283 return -ENOMEM;
284
285 regmap = devm_regmap_init_i2c(client, &mxc4005_regmap_config);
286 if (IS_ERR(regmap)) {
287 dev_err(&client->dev, "failed to initialize regmap\n");
288 return PTR_ERR(regmap);
289 }
290
291 data = iio_priv(indio_dev);
292 i2c_set_clientdata(client, indio_dev);
293 data->dev = &client->dev;
294 data->regmap = regmap;
295
296 ret = mxc4005_chip_init(data);
297 if (ret < 0) {
298 dev_err(&client->dev, "failed to initialize chip\n");
299 return ret;
300 }
301
302 mutex_init(&data->mutex);
303
304 indio_dev->dev.parent = &client->dev;
305 indio_dev->channels = mxc4005_channels;
306 indio_dev->num_channels = ARRAY_SIZE(mxc4005_channels);
307 indio_dev->name = MXC4005_DRV_NAME;
308 indio_dev->modes = INDIO_DIRECT_MODE;
309 indio_dev->info = &mxc4005_info;
310
311 ret = iio_device_register(indio_dev);
312 if (ret < 0) {
313 dev_err(&client->dev,
314 "unable to register iio device %d\n", ret);
315 return ret;
316 }
317
318 return 0;
319}
320
321static int mxc4005_remove(struct i2c_client *client)
322{
323 iio_device_unregister(i2c_get_clientdata(client));
324
325 return 0;
326}
327
328static const struct acpi_device_id mxc4005_acpi_match[] = {
329 {"MXC4005", 0},
330 { },
331};
332MODULE_DEVICE_TABLE(acpi, mxc4005_acpi_match);
333
334static const struct i2c_device_id mxc4005_id[] = {
335 {"mxc4005", 0},
336 { },
337};
338MODULE_DEVICE_TABLE(i2c, mxc4005_id);
339
340static struct i2c_driver mxc4005_driver = {
341 .driver = {
342 .name = MXC4005_DRV_NAME,
343 .acpi_match_table = ACPI_PTR(mxc4005_acpi_match),
344 },
345 .probe = mxc4005_probe,
346 .remove = mxc4005_remove,
347 .id_table = mxc4005_id,
348};
349
350module_i2c_driver(mxc4005_driver);
351
352MODULE_AUTHOR("Teodora Baluta <teodora.baluta@intel.com>");
353MODULE_LICENSE("GPL v2");
354MODULE_DESCRIPTION("MXC4005 3-axis accelerometer driver");