xref: /openbsd/sys/arch/macppc/dev/asms.c (revision 404b540a)
1 /*	$OpenBSD: asms.c,v 1.7 2008/04/25 16:37:44 xsa 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  * http://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 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
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
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
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