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