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