]>
Commit | Line | Data |
---|---|---|
569906e2 HG |
1 | /** |
2 | * IIO driver for the 3-axis accelerometer Domintech ARD10. | |
3 | * | |
4 | * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com> | |
5 | * Copyright (c) 2012 Domintech 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 DMARD10_REG_ACTR 0x00 | |
19 | #define DMARD10_REG_AFEM 0x0c | |
20 | #define DMARD10_REG_STADR 0x12 | |
21 | #define DMARD10_REG_STAINT 0x1c | |
22 | #define DMARD10_REG_MISC2 0x1f | |
23 | #define DMARD10_REG_PD 0x21 | |
24 | ||
25 | #define DMARD10_MODE_OFF 0x00 | |
26 | #define DMARD10_MODE_STANDBY 0x02 | |
27 | #define DMARD10_MODE_ACTIVE 0x06 | |
28 | #define DMARD10_MODE_READ_OTP 0x12 | |
29 | #define DMARD10_MODE_RESET_DATA_PATH 0x82 | |
30 | ||
31 | /* AFEN set 1, ATM[2:0]=b'000 (normal), EN_Z/Y/X/T=1 */ | |
32 | #define DMARD10_VALUE_AFEM_AFEN_NORMAL 0x8f | |
33 | /* ODR[3:0]=b'0111 (100Hz), CCK[3:0]=b'0100 (204.8kHZ) */ | |
34 | #define DMARD10_VALUE_CKSEL_ODR_100_204 0x74 | |
35 | /* INTC[6:5]=b'00 */ | |
36 | #define DMARD10_VALUE_INTC 0x00 | |
37 | /* TAP1/TAP2 Average 2 */ | |
38 | #define DMARD10_VALUE_TAPNS_AVE_2 0x11 | |
39 | ||
40 | #define DMARD10_VALUE_STADR 0x55 | |
41 | #define DMARD10_VALUE_STAINT 0xaa | |
42 | #define DMARD10_VALUE_MISC2_OSCA_EN 0x08 | |
43 | #define DMARD10_VALUE_PD_RST 0x52 | |
44 | ||
45 | /* Offsets into the buffer read in dmard10_read_raw() */ | |
46 | #define DMARD10_X_OFFSET 1 | |
47 | #define DMARD10_Y_OFFSET 2 | |
48 | #define DMARD10_Z_OFFSET 3 | |
49 | ||
50 | /* | |
51 | * a value of + or -128 corresponds to + or - 1G | |
52 | * scale = 9.81 / 128 = 0.076640625 | |
53 | */ | |
54 | ||
55 | static const int dmard10_nscale = 76640625; | |
56 | ||
57 | #define DMARD10_CHANNEL(reg, axis) { \ | |
58 | .type = IIO_ACCEL, \ | |
59 | .address = reg, \ | |
60 | .modified = 1, \ | |
61 | .channel2 = IIO_MOD_##axis, \ | |
62 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | |
63 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ | |
64 | } | |
65 | ||
66 | static const struct iio_chan_spec dmard10_channels[] = { | |
67 | DMARD10_CHANNEL(DMARD10_X_OFFSET, X), | |
68 | DMARD10_CHANNEL(DMARD10_Y_OFFSET, Y), | |
69 | DMARD10_CHANNEL(DMARD10_Z_OFFSET, Z), | |
70 | }; | |
71 | ||
72 | struct dmard10_data { | |
73 | struct i2c_client *client; | |
74 | }; | |
75 | ||
76 | /* Init sequence taken from the android driver */ | |
77 | static int dmard10_reset(struct i2c_client *client) | |
78 | { | |
79 | unsigned char buffer[7]; | |
80 | int ret; | |
81 | ||
82 | /* 1. Powerdown reset */ | |
83 | ret = i2c_smbus_write_byte_data(client, DMARD10_REG_PD, | |
84 | DMARD10_VALUE_PD_RST); | |
85 | if (ret < 0) | |
86 | return ret; | |
87 | ||
88 | /* | |
89 | * 2. ACTR => Standby mode => Download OTP to parameter reg => | |
90 | * Standby mode => Reset data path => Standby mode | |
91 | */ | |
92 | buffer[0] = DMARD10_REG_ACTR; | |
93 | buffer[1] = DMARD10_MODE_STANDBY; | |
94 | buffer[2] = DMARD10_MODE_READ_OTP; | |
95 | buffer[3] = DMARD10_MODE_STANDBY; | |
96 | buffer[4] = DMARD10_MODE_RESET_DATA_PATH; | |
97 | buffer[5] = DMARD10_MODE_STANDBY; | |
98 | ret = i2c_master_send(client, buffer, 6); | |
99 | if (ret < 0) | |
100 | return ret; | |
101 | ||
102 | /* 3. OSCA_EN = 1, TSTO = b'000 (INT1 = normal, TEST0 = normal) */ | |
103 | ret = i2c_smbus_write_byte_data(client, DMARD10_REG_MISC2, | |
104 | DMARD10_VALUE_MISC2_OSCA_EN); | |
105 | if (ret < 0) | |
106 | return ret; | |
107 | ||
108 | /* 4. AFEN = 1 (AFE will powerdown after ADC) */ | |
109 | buffer[0] = DMARD10_REG_AFEM; | |
110 | buffer[1] = DMARD10_VALUE_AFEM_AFEN_NORMAL; | |
111 | buffer[2] = DMARD10_VALUE_CKSEL_ODR_100_204; | |
112 | buffer[3] = DMARD10_VALUE_INTC; | |
113 | buffer[4] = DMARD10_VALUE_TAPNS_AVE_2; | |
114 | buffer[5] = 0x00; /* DLYC, no delay timing */ | |
115 | buffer[6] = 0x07; /* INTD=1 push-pull, INTA=1 active high, AUTOT=1 */ | |
116 | ret = i2c_master_send(client, buffer, 7); | |
117 | if (ret < 0) | |
118 | return ret; | |
119 | ||
120 | /* 5. Activation mode */ | |
121 | ret = i2c_smbus_write_byte_data(client, DMARD10_REG_ACTR, | |
122 | DMARD10_MODE_ACTIVE); | |
123 | if (ret < 0) | |
124 | return ret; | |
125 | ||
126 | return 0; | |
127 | } | |
128 | ||
129 | /* Shutdown sequence taken from the android driver */ | |
130 | static int dmard10_shutdown(struct i2c_client *client) | |
131 | { | |
132 | unsigned char buffer[3]; | |
133 | ||
134 | buffer[0] = DMARD10_REG_ACTR; | |
135 | buffer[1] = DMARD10_MODE_STANDBY; | |
136 | buffer[2] = DMARD10_MODE_OFF; | |
137 | ||
138 | return i2c_master_send(client, buffer, 3); | |
139 | } | |
140 | ||
141 | static int dmard10_read_raw(struct iio_dev *indio_dev, | |
142 | struct iio_chan_spec const *chan, | |
143 | int *val, int *val2, long mask) | |
144 | { | |
145 | struct dmard10_data *data = iio_priv(indio_dev); | |
146 | __le16 buf[4]; | |
147 | int ret; | |
148 | ||
149 | switch (mask) { | |
150 | case IIO_CHAN_INFO_RAW: | |
151 | /* | |
152 | * Read 8 bytes starting at the REG_STADR register, trying to | |
153 | * read the individual X, Y, Z registers will always read 0. | |
154 | */ | |
155 | ret = i2c_smbus_read_i2c_block_data(data->client, | |
156 | DMARD10_REG_STADR, | |
157 | sizeof(buf), (u8 *)buf); | |
158 | if (ret < 0) | |
159 | return ret; | |
160 | ret = le16_to_cpu(buf[chan->address]); | |
161 | *val = sign_extend32(ret, 12); | |
162 | return IIO_VAL_INT; | |
163 | case IIO_CHAN_INFO_SCALE: | |
164 | *val = 0; | |
165 | *val2 = dmard10_nscale; | |
166 | return IIO_VAL_INT_PLUS_NANO; | |
167 | default: | |
168 | return -EINVAL; | |
169 | } | |
170 | } | |
171 | ||
172 | static const struct iio_info dmard10_info = { | |
569906e2 HG |
173 | .read_raw = dmard10_read_raw, |
174 | }; | |
175 | ||
176 | static int dmard10_probe(struct i2c_client *client, | |
177 | const struct i2c_device_id *id) | |
178 | { | |
179 | int ret; | |
180 | struct iio_dev *indio_dev; | |
181 | struct dmard10_data *data; | |
182 | ||
183 | /* These 2 registers have special POR reset values used for id */ | |
184 | ret = i2c_smbus_read_byte_data(client, DMARD10_REG_STADR); | |
185 | if (ret != DMARD10_VALUE_STADR) | |
186 | return (ret < 0) ? ret : -ENODEV; | |
187 | ||
188 | ret = i2c_smbus_read_byte_data(client, DMARD10_REG_STAINT); | |
189 | if (ret != DMARD10_VALUE_STAINT) | |
190 | return (ret < 0) ? ret : -ENODEV; | |
191 | ||
192 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); | |
193 | if (!indio_dev) { | |
194 | dev_err(&client->dev, "iio allocation failed!\n"); | |
195 | return -ENOMEM; | |
196 | } | |
197 | ||
198 | data = iio_priv(indio_dev); | |
199 | data->client = client; | |
200 | i2c_set_clientdata(client, indio_dev); | |
201 | ||
202 | indio_dev->dev.parent = &client->dev; | |
203 | indio_dev->info = &dmard10_info; | |
204 | indio_dev->name = "dmard10"; | |
205 | indio_dev->modes = INDIO_DIRECT_MODE; | |
206 | indio_dev->channels = dmard10_channels; | |
207 | indio_dev->num_channels = ARRAY_SIZE(dmard10_channels); | |
208 | ||
209 | ret = dmard10_reset(client); | |
210 | if (ret < 0) | |
211 | return ret; | |
212 | ||
213 | ret = iio_device_register(indio_dev); | |
214 | if (ret < 0) { | |
215 | dev_err(&client->dev, "device_register failed\n"); | |
216 | dmard10_shutdown(client); | |
217 | } | |
218 | ||
219 | return ret; | |
220 | } | |
221 | ||
222 | static int dmard10_remove(struct i2c_client *client) | |
223 | { | |
224 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | |
225 | ||
226 | iio_device_unregister(indio_dev); | |
227 | ||
228 | return dmard10_shutdown(client); | |
229 | } | |
230 | ||
231 | #ifdef CONFIG_PM_SLEEP | |
232 | static int dmard10_suspend(struct device *dev) | |
233 | { | |
234 | return dmard10_shutdown(to_i2c_client(dev)); | |
235 | } | |
236 | ||
237 | static int dmard10_resume(struct device *dev) | |
238 | { | |
239 | return dmard10_reset(to_i2c_client(dev)); | |
240 | } | |
241 | #endif | |
242 | ||
243 | static SIMPLE_DEV_PM_OPS(dmard10_pm_ops, dmard10_suspend, dmard10_resume); | |
244 | ||
245 | static const struct i2c_device_id dmard10_i2c_id[] = { | |
246 | {"dmard10", 0}, | |
247 | {} | |
248 | }; | |
249 | MODULE_DEVICE_TABLE(i2c, dmard10_i2c_id); | |
250 | ||
251 | static struct i2c_driver dmard10_driver = { | |
252 | .driver = { | |
253 | .name = "dmard10", | |
254 | .pm = &dmard10_pm_ops, | |
255 | }, | |
256 | .probe = dmard10_probe, | |
257 | .remove = dmard10_remove, | |
258 | .id_table = dmard10_i2c_id, | |
259 | }; | |
260 | ||
261 | module_i2c_driver(dmard10_driver); | |
262 | ||
263 | MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); | |
264 | MODULE_DESCRIPTION("Domintech ARD10 3-Axis Accelerometer driver"); | |
265 | MODULE_LICENSE("GPL v2"); |