Skip to content

Commit 598893e

Browse files
jwrdegoedejic23
authored andcommitted
iio: accel: Add driver for the MiraMEMS DA311 3-axis 12-bit digital accelerometer
This driver is based on the DA311 Android driver which can be found here: https://git.matricom.net/Firmware/kernel_amlogic_meson-common/tree/1e70113a5befd07debb68f537156def84c5be57a/drivers/amlogic/input/sensor the mir3da_* files are the DA311 driver. Unfortunately there is no datasheet. Signed-off-by: Hans de Goede <[email protected]> Acked-by: Rob Herring <[email protected]> Signed-off-by: Jonathan Cameron <[email protected]>
1 parent 569906e commit 598893e

File tree

4 files changed

+317
-0
lines changed

4 files changed

+317
-0
lines changed

Documentation/devicetree/bindings/i2c/trivial-devices.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ microchip,mcp4662-502 Microchip 8-bit Dual I2C Digital Potentiometer with NV Mem
125125
microchip,mcp4662-103 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (10k)
126126
microchip,mcp4662-503 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (50k)
127127
microchip,mcp4662-104 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (100k)
128+
miramems,da311 MiraMEMS DA311 3-axis 12-bit digital accelerometer
128129
national,lm63 Temperature sensor with integrated fan control
129130
national,lm75 I2C TEMP SENSOR
130131
national,lm80 Serial Interface ACPI-Compatible Microprocessor System Hardware Monitor

drivers/iio/accel/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ config BMC150_ACCEL_SPI
5252
tristate
5353
select REGMAP_SPI
5454

55+
config DA311
56+
tristate "MiraMEMS DA311 3-axis 12-bit digital accelerometer driver"
57+
depends on I2C
58+
help
59+
Say yes here to build support for the MiraMEMS DA311 3-axis 12-bit
60+
digital accelerometer.
61+
62+
To compile this driver as a module, choose M here: the
63+
module will be called da311.
64+
5565
config DMARD06
5666
tristate "Domintech DMARD06 Digital Accelerometer Driver"
5767
depends on OF || COMPILE_TEST

drivers/iio/accel/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ obj-$(CONFIG_BMA220) += bma220_spi.o
88
obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o
99
obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o
1010
obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o
11+
obj-$(CONFIG_DA311) += da311.o
1112
obj-$(CONFIG_DMARD06) += dmard06.o
1213
obj-$(CONFIG_DMARD09) += dmard09.o
1314
obj-$(CONFIG_DMARD10) += dmard10.o

drivers/iio/accel/da311.c

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
/**
2+
* IIO driver for the MiraMEMS DA311 3-axis accelerometer
3+
*
4+
* Copyright (c) 2016 Hans de Goede <[email protected]>
5+
* Copyright (c) 2011-2013 MiraMEMS Sensing Technology Co., Ltd.
6+
*
7+
* This program is free software; you can redistribute it and/or modify it
8+
* under the terms and conditions of the GNU General Public License,
9+
* version 2, as published by the Free Software Foundation.
10+
*/
11+
12+
#include <linux/module.h>
13+
#include <linux/i2c.h>
14+
#include <linux/iio/iio.h>
15+
#include <linux/iio/sysfs.h>
16+
#include <linux/byteorder/generic.h>
17+
18+
#define DA311_CHIP_ID 0x13
19+
20+
/*
21+
* Note register addressed go from 0 - 0x3f and then wrap.
22+
* For some reason there are 2 banks with 0 - 0x3f addresses,
23+
* rather then a single 0-0x7f bank.
24+
*/
25+
26+
/* Bank 0 regs */
27+
#define DA311_REG_BANK 0x0000
28+
#define DA311_REG_LDO_REG 0x0006
29+
#define DA311_REG_CHIP_ID 0x000f
30+
#define DA311_REG_TEMP_CFG_REG 0x001f
31+
#define DA311_REG_CTRL_REG1 0x0020
32+
#define DA311_REG_CTRL_REG3 0x0022
33+
#define DA311_REG_CTRL_REG4 0x0023
34+
#define DA311_REG_CTRL_REG5 0x0024
35+
#define DA311_REG_CTRL_REG6 0x0025
36+
#define DA311_REG_STATUS_REG 0x0027
37+
#define DA311_REG_OUT_X_L 0x0028
38+
#define DA311_REG_OUT_X_H 0x0029
39+
#define DA311_REG_OUT_Y_L 0x002a
40+
#define DA311_REG_OUT_Y_H 0x002b
41+
#define DA311_REG_OUT_Z_L 0x002c
42+
#define DA311_REG_OUT_Z_H 0x002d
43+
#define DA311_REG_INT1_CFG 0x0030
44+
#define DA311_REG_INT1_SRC 0x0031
45+
#define DA311_REG_INT1_THS 0x0032
46+
#define DA311_REG_INT1_DURATION 0x0033
47+
#define DA311_REG_INT2_CFG 0x0034
48+
#define DA311_REG_INT2_SRC 0x0035
49+
#define DA311_REG_INT2_THS 0x0036
50+
#define DA311_REG_INT2_DURATION 0x0037
51+
#define DA311_REG_CLICK_CFG 0x0038
52+
#define DA311_REG_CLICK_SRC 0x0039
53+
#define DA311_REG_CLICK_THS 0x003a
54+
#define DA311_REG_TIME_LIMIT 0x003b
55+
#define DA311_REG_TIME_LATENCY 0x003c
56+
#define DA311_REG_TIME_WINDOW 0x003d
57+
58+
/* Bank 1 regs */
59+
#define DA311_REG_SOFT_RESET 0x0105
60+
#define DA311_REG_OTP_XOFF_L 0x0110
61+
#define DA311_REG_OTP_XOFF_H 0x0111
62+
#define DA311_REG_OTP_YOFF_L 0x0112
63+
#define DA311_REG_OTP_YOFF_H 0x0113
64+
#define DA311_REG_OTP_ZOFF_L 0x0114
65+
#define DA311_REG_OTP_ZOFF_H 0x0115
66+
#define DA311_REG_OTP_XSO 0x0116
67+
#define DA311_REG_OTP_YSO 0x0117
68+
#define DA311_REG_OTP_ZSO 0x0118
69+
#define DA311_REG_OTP_TRIM_OSC 0x011b
70+
#define DA311_REG_LPF_ABSOLUTE 0x011c
71+
#define DA311_REG_TEMP_OFF1 0x0127
72+
#define DA311_REG_TEMP_OFF2 0x0128
73+
#define DA311_REG_TEMP_OFF3 0x0129
74+
#define DA311_REG_OTP_TRIM_THERM_H 0x011a
75+
76+
/*
77+
* a value of + or -1024 corresponds to + or - 1G
78+
* scale = 9.81 / 1024 = 0.009580078
79+
*/
80+
81+
static const int da311_nscale = 9580078;
82+
83+
#define DA311_CHANNEL(reg, axis) { \
84+
.type = IIO_ACCEL, \
85+
.address = reg, \
86+
.modified = 1, \
87+
.channel2 = IIO_MOD_##axis, \
88+
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
89+
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
90+
}
91+
92+
static const struct iio_chan_spec da311_channels[] = {
93+
/* | 0x80 comes from the android driver */
94+
DA311_CHANNEL(DA311_REG_OUT_X_L | 0x80, X),
95+
DA311_CHANNEL(DA311_REG_OUT_Y_L | 0x80, Y),
96+
DA311_CHANNEL(DA311_REG_OUT_Z_L | 0x80, Z),
97+
};
98+
99+
struct da311_data {
100+
struct i2c_client *client;
101+
};
102+
103+
static int da311_register_mask_write(struct i2c_client *client, u16 addr,
104+
u8 mask, u8 data)
105+
{
106+
int ret;
107+
u8 tmp_data = 0;
108+
109+
if (addr & 0xff00) {
110+
/* Select bank 1 */
111+
ret = i2c_smbus_write_byte_data(client, DA311_REG_BANK, 0x01);
112+
if (ret < 0)
113+
return ret;
114+
}
115+
116+
if (mask != 0xff) {
117+
ret = i2c_smbus_read_byte_data(client, addr);
118+
if (ret < 0)
119+
return ret;
120+
tmp_data = ret;
121+
}
122+
123+
tmp_data &= ~mask;
124+
tmp_data |= data & mask;
125+
ret = i2c_smbus_write_byte_data(client, addr & 0xff, tmp_data);
126+
if (ret < 0)
127+
return ret;
128+
129+
if (addr & 0xff00) {
130+
/* Back to bank 0 */
131+
ret = i2c_smbus_write_byte_data(client, DA311_REG_BANK, 0x00);
132+
if (ret < 0)
133+
return ret;
134+
}
135+
136+
return 0;
137+
}
138+
139+
/* Init sequence taken from the android driver */
140+
static int da311_reset(struct i2c_client *client)
141+
{
142+
const struct {
143+
u16 addr;
144+
u8 mask;
145+
u8 data;
146+
} init_data[] = {
147+
{ DA311_REG_TEMP_CFG_REG, 0xff, 0x08 },
148+
{ DA311_REG_CTRL_REG5, 0xff, 0x80 },
149+
{ DA311_REG_CTRL_REG4, 0x30, 0x00 },
150+
{ DA311_REG_CTRL_REG1, 0xff, 0x6f },
151+
{ DA311_REG_TEMP_CFG_REG, 0xff, 0x88 },
152+
{ DA311_REG_LDO_REG, 0xff, 0x02 },
153+
{ DA311_REG_OTP_TRIM_OSC, 0xff, 0x27 },
154+
{ DA311_REG_LPF_ABSOLUTE, 0xff, 0x30 },
155+
{ DA311_REG_TEMP_OFF1, 0xff, 0x3f },
156+
{ DA311_REG_TEMP_OFF2, 0xff, 0xff },
157+
{ DA311_REG_TEMP_OFF3, 0xff, 0x0f },
158+
};
159+
int i, ret;
160+
161+
/* Reset */
162+
ret = da311_register_mask_write(client, DA311_REG_SOFT_RESET,
163+
0xff, 0xaa);
164+
if (ret < 0)
165+
return ret;
166+
167+
for (i = 0; i < ARRAY_SIZE(init_data); i++) {
168+
ret = da311_register_mask_write(client,
169+
init_data[i].addr,
170+
init_data[i].mask,
171+
init_data[i].data);
172+
if (ret < 0)
173+
return ret;
174+
}
175+
176+
return 0;
177+
}
178+
179+
static int da311_enable(struct i2c_client *client, bool enable)
180+
{
181+
u8 data = enable ? 0x00 : 0x20;
182+
183+
return da311_register_mask_write(client, DA311_REG_TEMP_CFG_REG,
184+
0x20, data);
185+
}
186+
187+
static int da311_read_raw(struct iio_dev *indio_dev,
188+
struct iio_chan_spec const *chan,
189+
int *val, int *val2, long mask)
190+
{
191+
struct da311_data *data = iio_priv(indio_dev);
192+
int ret;
193+
194+
switch (mask) {
195+
case IIO_CHAN_INFO_RAW:
196+
ret = i2c_smbus_read_word_data(data->client, chan->address);
197+
if (ret < 0)
198+
return ret;
199+
/*
200+
* Values are 12 bits, stored as 16 bits with the 4
201+
* least significant bits always 0.
202+
*/
203+
*val = (short)ret >> 4;
204+
return IIO_VAL_INT;
205+
case IIO_CHAN_INFO_SCALE:
206+
*val = 0;
207+
*val2 = da311_nscale;
208+
return IIO_VAL_INT_PLUS_NANO;
209+
default:
210+
return -EINVAL;
211+
}
212+
}
213+
214+
static const struct iio_info da311_info = {
215+
.driver_module = THIS_MODULE,
216+
.read_raw = da311_read_raw,
217+
};
218+
219+
static int da311_probe(struct i2c_client *client,
220+
const struct i2c_device_id *id)
221+
{
222+
int ret;
223+
struct iio_dev *indio_dev;
224+
struct da311_data *data;
225+
226+
ret = i2c_smbus_read_byte_data(client, DA311_REG_CHIP_ID);
227+
if (ret != DA311_CHIP_ID)
228+
return (ret < 0) ? ret : -ENODEV;
229+
230+
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
231+
if (!indio_dev)
232+
return -ENOMEM;
233+
234+
data = iio_priv(indio_dev);
235+
data->client = client;
236+
i2c_set_clientdata(client, indio_dev);
237+
238+
indio_dev->dev.parent = &client->dev;
239+
indio_dev->info = &da311_info;
240+
indio_dev->name = "da311";
241+
indio_dev->modes = INDIO_DIRECT_MODE;
242+
indio_dev->channels = da311_channels;
243+
indio_dev->num_channels = ARRAY_SIZE(da311_channels);
244+
245+
ret = da311_reset(client);
246+
if (ret < 0)
247+
return ret;
248+
249+
ret = da311_enable(client, true);
250+
if (ret < 0)
251+
return ret;
252+
253+
ret = iio_device_register(indio_dev);
254+
if (ret < 0) {
255+
dev_err(&client->dev, "device_register failed\n");
256+
da311_enable(client, false);
257+
}
258+
259+
return ret;
260+
}
261+
262+
static int da311_remove(struct i2c_client *client)
263+
{
264+
struct iio_dev *indio_dev = i2c_get_clientdata(client);
265+
266+
iio_device_unregister(indio_dev);
267+
268+
return da311_enable(client, false);
269+
}
270+
271+
#ifdef CONFIG_PM_SLEEP
272+
static int da311_suspend(struct device *dev)
273+
{
274+
return da311_enable(to_i2c_client(dev), false);
275+
}
276+
277+
static int da311_resume(struct device *dev)
278+
{
279+
return da311_enable(to_i2c_client(dev), true);
280+
}
281+
#endif
282+
283+
static SIMPLE_DEV_PM_OPS(da311_pm_ops, da311_suspend, da311_resume);
284+
285+
static const struct i2c_device_id da311_i2c_id[] = {
286+
{"da311", 0},
287+
{}
288+
};
289+
MODULE_DEVICE_TABLE(i2c, da311_i2c_id);
290+
291+
static struct i2c_driver da311_driver = {
292+
.driver = {
293+
.name = "da311",
294+
.pm = &da311_pm_ops,
295+
},
296+
.probe = da311_probe,
297+
.remove = da311_remove,
298+
.id_table = da311_i2c_id,
299+
};
300+
301+
module_i2c_driver(da311_driver);
302+
303+
MODULE_AUTHOR("Hans de Goede <[email protected]>");
304+
MODULE_DESCRIPTION("MiraMEMS DA311 3-Axis Accelerometer driver");
305+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)