1 /* $OpenBSD: asms.c,v 1.9 2023/12/26 14:04:50 miod Exp $ */
2 /*
3 * Copyright (c) 2005 Xavier Santolaria <xsa@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 /*
19 * A driver for the Apple Sudden Motion Sensor based on notes from
20 * https://johannes.sipsolutions.net/PowerBook/apple-motion-sensor-specification
21 */
22
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/device.h>
26 #include <sys/sensors.h>
27
28 #include <dev/i2c/i2cvar.h>
29
30 /* ASMS Registers */
31 #define ASMS_REG_COMMAND 0x00
32 #define ASMS_REG_STATUS 0x01
33 #define ASMS_REG_RCONTROL1 0x02
34 #define ASMS_REG_RCONTROL2 0x03
35 #define ASMS_REG_RCONTROL3 0x04
36 #define ASMS_REG_RDATA1 0x05
37 #define ASMS_REG_RDATA2 0x06
38 #define ASMS_REG_DATA_X 0x20
39 #define ASMS_REG_DATA_Y 0x21
40 #define ASMS_REG_DATA_Z 0x22
41 #define ASMS_REG_SENS_LOW 0x26 /* init with 0x15 */
42 #define ASMS_REG_SENS_HIGH 0x27 /* init with 0x60 */
43 #define ASMS_REG_CONTROL_X 0x28 /* init with 0x08 */
44 #define ASMS_REG_CONTROL_Y 0x29 /* init with 0x0f */
45 #define ASMS_REG_CONTROL_Z 0x2a /* init with 0x4f */
46 #define ASMS_REG_UNKNOWN1 0x2b /* init with 0x14 */
47 #define ASMS_REG_VENDOR 0x2e
48 #define ASMS_CMD_READ_VER 0x01
49 #define ASMS_CMD_READ_MEM 0x02
50 #define ASMS_CMD_RESET 0x07
51 #define ASMS_CMD_START 0x08
52
53 /* Sensors */
54 #define ASMS_DATA_X 0
55 #define ASMS_DATA_Y 1
56 #define ASMS_DATA_Z 2
57 #define ASMS_NUM_SENSORS 3
58
59 struct asms_softc {
60 struct device sc_dev;
61 i2c_tag_t sc_tag;
62 i2c_addr_t sc_addr;
63
64 struct ksensor sc_sensor[ASMS_NUM_SENSORS];
65 struct ksensordev sc_sensordev;
66 };
67
68 int asms_match(struct device *, void *, void *);
69 void asms_attach(struct device *, struct device *, void *);
70 void asms_refresh(void *);
71
72 const struct cfattach asms_ca = {
73 sizeof(struct asms_softc), asms_match, asms_attach
74 };
75
76 struct cfdriver asms_cd = {
77 NULL, "asms", DV_DULL
78 };
79
80 int
asms_match(struct device * parent,void * match,void * aux)81 asms_match(struct device *parent, void *match, void *aux)
82 {
83 struct i2c_attach_args *ia = aux;
84
85 if (strcmp(ia->ia_name, "AAPL,accelerometer_1") == 0)
86 return (1);
87 return (0);
88 }
89
90 void
asms_attach(struct device * parent,struct device * self,void * aux)91 asms_attach(struct device *parent, struct device *self, void *aux)
92 {
93 struct asms_softc *sc = (struct asms_softc *)self;
94 struct i2c_attach_args *ia = aux;
95 u_int8_t cmd, data, rev1, rev2, ver1, ver2;
96 int i, vflag = 0;
97
98 sc->sc_tag = ia->ia_tag;
99 sc->sc_addr = ia ->ia_addr;
100
101 iic_acquire_bus(sc->sc_tag, 0);
102
103 cmd = ASMS_REG_COMMAND; data = ASMS_CMD_START;
104 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
105 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
106 iic_release_bus(sc->sc_tag, 0);
107 printf(": cannot write command register\n");
108 return;
109 }
110 delay(10000);
111
112 cmd = ASMS_REG_RCONTROL1; data = 0x02;
113 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
114 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
115 iic_release_bus(sc->sc_tag, 0);
116 printf(": cannot write read control register\n");
117 return;
118 }
119
120 cmd = ASMS_REG_RCONTROL2; data = 0x85;
121 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
122 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
123 iic_release_bus(sc->sc_tag, 0);
124 printf(": cannot write read control register\n");
125 return;
126 }
127
128 cmd = ASMS_REG_RCONTROL3; data = 0x01;
129 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
130 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
131 iic_release_bus(sc->sc_tag, 0);
132 printf(": cannot write read control register\n");
133 return;
134 }
135
136 cmd = ASMS_REG_COMMAND; data = ASMS_CMD_READ_MEM;
137 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
138 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
139 iic_release_bus(sc->sc_tag, 0);
140 printf(": cannot write command register\n");
141 return;
142 }
143 delay(10000);
144
145 cmd = ASMS_REG_RDATA1;
146 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
147 sc->sc_addr, &cmd, sizeof cmd, &rev1, sizeof rev1, 0)) {
148 iic_release_bus(sc->sc_tag, 0);
149 printf(": cannot read data register\n");
150 return;
151 }
152
153 cmd = ASMS_REG_RDATA2;
154 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
155 sc->sc_addr, &cmd, sizeof cmd, &rev2, sizeof rev2, 0)) {
156 iic_release_bus(sc->sc_tag, 0);
157 printf(": cannot read data register\n");
158 return;
159 }
160
161 cmd = ASMS_REG_COMMAND; data = ASMS_CMD_READ_VER;
162 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
163 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
164 iic_release_bus(sc->sc_tag, 0);
165 printf(": cannot write command register\n");
166 return;
167 }
168 delay(10000);
169
170 cmd = ASMS_REG_RDATA1;
171 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
172 sc->sc_addr, &cmd, sizeof cmd, &ver1, sizeof ver1, 0)) {
173 iic_release_bus(sc->sc_tag, 0);
174 printf(": cannot read data register\n");
175 return;
176 }
177
178 cmd = ASMS_REG_RDATA2;
179 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
180 sc->sc_addr, &cmd, sizeof cmd, &ver2, sizeof ver2, 0)) {
181 iic_release_bus(sc->sc_tag, 0);
182 printf(": cannot read data register\n");
183 return;
184 }
185
186 cmd = ASMS_REG_VENDOR;
187 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
188 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
189 iic_release_bus(sc->sc_tag, 0);
190 printf(": cannot read vendor register\n");
191 return;
192 }
193 if (data & 0x10)
194 vflag = 1;
195
196 cmd = ASMS_REG_SENS_LOW; data = 0x15;
197 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
198 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
199 iic_release_bus(sc->sc_tag, 0);
200 printf(": cannot write sensibility low register\n");
201 return;
202 }
203
204 cmd = ASMS_REG_SENS_HIGH; data = 0x60;
205 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
206 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
207 iic_release_bus(sc->sc_tag, 0);
208 printf(": cannot write sensibility high register\n");
209 return;
210 }
211
212 cmd = ASMS_REG_CONTROL_X; data = 0x08;
213 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
214 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
215 iic_release_bus(sc->sc_tag, 0);
216 printf(": cannot write control X register\n");
217 return;
218 }
219
220 cmd = ASMS_REG_CONTROL_Y; data= 0x0f;
221 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
222 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
223 iic_release_bus(sc->sc_tag, 0);
224 printf(": cannot write control Y register\n");
225 return;
226 }
227
228 cmd = ASMS_REG_CONTROL_Z; data = 0x4f;
229 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
230 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
231 iic_release_bus(sc->sc_tag, 0);
232 printf(": cannot write control Z register\n");
233 return;
234 }
235
236 cmd = ASMS_REG_UNKNOWN1; data = 0x14;
237 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
238 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
239 iic_release_bus(sc->sc_tag, 0);
240 printf(": cannot write unknown 1 register\n");
241 return;
242 }
243
244 iic_release_bus(sc->sc_tag, 0);
245
246 printf(": rev %x.%x, version %x.%x", rev1, rev2, ver1, ver2);
247
248 /* Initialize sensor data. */
249 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
250 sizeof(sc->sc_sensordev.xname));
251
252 sc->sc_sensor[ASMS_DATA_X].type = SENSOR_INTEGER;
253 strlcpy(sc->sc_sensor[ASMS_DATA_X].desc, "X_ACCEL",
254 sizeof(sc->sc_sensor[ASMS_DATA_X].desc));
255
256 sc->sc_sensor[ASMS_DATA_Y].type = SENSOR_INTEGER;
257 strlcpy(sc->sc_sensor[ASMS_DATA_Y].desc, "Y_ACCEL",
258 sizeof(sc->sc_sensor[ASMS_DATA_Y].desc));
259
260 sc->sc_sensor[ASMS_DATA_Z].type = SENSOR_INTEGER;
261 strlcpy(sc->sc_sensor[ASMS_DATA_Z].desc, "Z_ACCEL",
262 sizeof(sc->sc_sensor[ASMS_DATA_Z].desc));
263
264 if (sensor_task_register(sc, asms_refresh, 5) == NULL) {
265 printf(": unable to register update task\n");
266 return;
267 }
268
269 for (i = 0; i < ASMS_NUM_SENSORS; i++)
270 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
271 sensordev_install(&sc->sc_sensordev);
272
273 printf("\n");
274 }
275
276 void
asms_refresh(void * arg)277 asms_refresh(void *arg)
278 {
279 struct asms_softc *sc = arg;
280 u_int8_t cmd;
281 int8_t sdata;
282
283 iic_acquire_bus(sc->sc_tag, 0);
284
285 cmd = ASMS_REG_DATA_X;
286 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
287 sc->sc_addr, &cmd, sizeof cmd, &sdata, sizeof sdata, 0) == 0)
288 sc->sc_sensor[ASMS_DATA_X].value = sdata;
289
290 cmd = ASMS_REG_DATA_Y;
291 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
292 sc->sc_addr, &cmd, sizeof cmd, &sdata, sizeof sdata, 0) == 0)
293 sc->sc_sensor[ASMS_DATA_Y].value = sdata;
294
295 cmd = ASMS_REG_DATA_Z;
296 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
297 sc->sc_addr, &cmd, sizeof cmd, &sdata, sizeof sdata, 0) == 0)
298 sc->sc_sensor[ASMS_DATA_Z].value = sdata;
299
300 iic_release_bus(sc->sc_tag, 0);
301 }
302