Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ams-i2c.c
Go to the documentation of this file.
1 /*
2  * Apple Motion Sensor driver (I2C variant)
3  *
4  * Copyright (C) 2005 Stelian Pop ([email protected])
5  * Copyright (C) 2006 Michael Hanselmann ([email protected])
6  *
7  * Clean room implementation based on the reverse engineered Mac OS X driver by
8  * Johannes Berg <[email protected]>, documentation available at
9  * http://johannes.sipsolutions.net/PowerBook/Apple_Motion_Sensor_Specification
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  */
16 
17 #include <linux/module.h>
18 #include <linux/types.h>
19 #include <linux/errno.h>
20 #include <linux/init.h>
21 #include <linux/delay.h>
22 
23 #include "ams.h"
24 
25 /* AMS registers */
26 #define AMS_COMMAND 0x00 /* command register */
27 #define AMS_STATUS 0x01 /* status register */
28 #define AMS_CTRL1 0x02 /* read control 1 (number of values) */
29 #define AMS_CTRL2 0x03 /* read control 2 (offset?) */
30 #define AMS_CTRL3 0x04 /* read control 3 (size of each value?) */
31 #define AMS_DATA1 0x05 /* read data 1 */
32 #define AMS_DATA2 0x06 /* read data 2 */
33 #define AMS_DATA3 0x07 /* read data 3 */
34 #define AMS_DATA4 0x08 /* read data 4 */
35 #define AMS_DATAX 0x20 /* data X */
36 #define AMS_DATAY 0x21 /* data Y */
37 #define AMS_DATAZ 0x22 /* data Z */
38 #define AMS_FREEFALL 0x24 /* freefall int control */
39 #define AMS_SHOCK 0x25 /* shock int control */
40 #define AMS_SENSLOW 0x26 /* sensitivity low limit */
41 #define AMS_SENSHIGH 0x27 /* sensitivity high limit */
42 #define AMS_CTRLX 0x28 /* control X */
43 #define AMS_CTRLY 0x29 /* control Y */
44 #define AMS_CTRLZ 0x2A /* control Z */
45 #define AMS_UNKNOWN1 0x2B /* unknown 1 */
46 #define AMS_UNKNOWN2 0x2C /* unknown 2 */
47 #define AMS_UNKNOWN3 0x2D /* unknown 3 */
48 #define AMS_VENDOR 0x2E /* vendor */
49 
50 /* AMS commands - use with the AMS_COMMAND register */
61 };
62 
63 static int ams_i2c_probe(struct i2c_client *client,
64  const struct i2c_device_id *id);
65 static int ams_i2c_remove(struct i2c_client *client);
66 
67 static const struct i2c_device_id ams_id[] = {
68  { "MAC,accelerometer_1", 0 },
69  { }
70 };
71 MODULE_DEVICE_TABLE(i2c, ams_id);
72 
73 static struct i2c_driver ams_i2c_driver = {
74  .driver = {
75  .name = "ams",
76  .owner = THIS_MODULE,
77  },
78  .probe = ams_i2c_probe,
79  .remove = ams_i2c_remove,
80  .id_table = ams_id,
81 };
82 
83 static s32 ams_i2c_read(u8 reg)
84 {
85  return i2c_smbus_read_byte_data(ams_info.i2c_client, reg);
86 }
87 
88 static int ams_i2c_write(u8 reg, u8 value)
89 {
90  return i2c_smbus_write_byte_data(ams_info.i2c_client, reg, value);
91 }
92 
93 static int ams_i2c_cmd(enum ams_i2c_cmd cmd)
94 {
95  s32 result;
96  int count = 3;
97 
98  ams_i2c_write(AMS_COMMAND, cmd);
99  msleep(5);
100 
101  while (count--) {
102  result = ams_i2c_read(AMS_COMMAND);
103  if (result == 0 || result & 0x80)
104  return 0;
105 
107  }
108 
109  return -1;
110 }
111 
112 static void ams_i2c_set_irq(enum ams_irq reg, char enable)
113 {
114  if (reg & AMS_IRQ_FREEFALL) {
115  u8 val = ams_i2c_read(AMS_CTRLX);
116  if (enable)
117  val |= 0x80;
118  else
119  val &= ~0x80;
120  ams_i2c_write(AMS_CTRLX, val);
121  }
122 
123  if (reg & AMS_IRQ_SHOCK) {
124  u8 val = ams_i2c_read(AMS_CTRLY);
125  if (enable)
126  val |= 0x80;
127  else
128  val &= ~0x80;
129  ams_i2c_write(AMS_CTRLY, val);
130  }
131 
132  if (reg & AMS_IRQ_GLOBAL) {
133  u8 val = ams_i2c_read(AMS_CTRLZ);
134  if (enable)
135  val |= 0x80;
136  else
137  val &= ~0x80;
138  ams_i2c_write(AMS_CTRLZ, val);
139  }
140 }
141 
142 static void ams_i2c_clear_irq(enum ams_irq reg)
143 {
144  if (reg & AMS_IRQ_FREEFALL)
145  ams_i2c_write(AMS_FREEFALL, 0);
146 
147  if (reg & AMS_IRQ_SHOCK)
148  ams_i2c_write(AMS_SHOCK, 0);
149 }
150 
151 static u8 ams_i2c_get_vendor(void)
152 {
153  return ams_i2c_read(AMS_VENDOR);
154 }
155 
156 static void ams_i2c_get_xyz(s8 *x, s8 *y, s8 *z)
157 {
158  *x = ams_i2c_read(AMS_DATAX);
159  *y = ams_i2c_read(AMS_DATAY);
160  *z = ams_i2c_read(AMS_DATAZ);
161 }
162 
163 static int ams_i2c_probe(struct i2c_client *client,
164  const struct i2c_device_id *id)
165 {
166  int vmaj, vmin;
167  int result;
168 
169  /* There can be only one */
170  if (unlikely(ams_info.has_device))
171  return -ENODEV;
172 
173  ams_info.i2c_client = client;
174 
175  if (ams_i2c_cmd(AMS_CMD_RESET)) {
176  printk(KERN_INFO "ams: Failed to reset the device\n");
177  return -ENODEV;
178  }
179 
180  if (ams_i2c_cmd(AMS_CMD_START)) {
181  printk(KERN_INFO "ams: Failed to start the device\n");
182  return -ENODEV;
183  }
184 
185  /* get version/vendor information */
186  ams_i2c_write(AMS_CTRL1, 0x02);
187  ams_i2c_write(AMS_CTRL2, 0x85);
188  ams_i2c_write(AMS_CTRL3, 0x01);
189 
191 
192  vmaj = ams_i2c_read(AMS_DATA1);
193  vmin = ams_i2c_read(AMS_DATA2);
194  if (vmaj != 1 || vmin != 52) {
195  printk(KERN_INFO "ams: Incorrect device version (%d.%d)\n",
196  vmaj, vmin);
197  return -ENODEV;
198  }
199 
201 
202  vmaj = ams_i2c_read(AMS_DATA1);
203  vmin = ams_i2c_read(AMS_DATA2);
204  if (vmaj != 0 || vmin != 1) {
205  printk(KERN_INFO "ams: Incorrect firmware version (%d.%d)\n",
206  vmaj, vmin);
207  return -ENODEV;
208  }
209 
210  /* Disable interrupts */
211  ams_i2c_set_irq(AMS_IRQ_ALL, 0);
212 
213  result = ams_sensor_attach();
214  if (result < 0)
215  return result;
216 
217  /* Set default values */
218  ams_i2c_write(AMS_SENSLOW, 0x15);
219  ams_i2c_write(AMS_SENSHIGH, 0x60);
220  ams_i2c_write(AMS_CTRLX, 0x08);
221  ams_i2c_write(AMS_CTRLY, 0x0F);
222  ams_i2c_write(AMS_CTRLZ, 0x4F);
223  ams_i2c_write(AMS_UNKNOWN1, 0x14);
224 
225  /* Clear interrupts */
226  ams_i2c_clear_irq(AMS_IRQ_ALL);
227 
228  ams_info.has_device = 1;
229 
230  /* Enable interrupts */
231  ams_i2c_set_irq(AMS_IRQ_ALL, 1);
232 
233  printk(KERN_INFO "ams: Found I2C based motion sensor\n");
234 
235  return 0;
236 }
237 
238 static int ams_i2c_remove(struct i2c_client *client)
239 {
240  if (ams_info.has_device) {
242 
243  /* Disable interrupts */
244  ams_i2c_set_irq(AMS_IRQ_ALL, 0);
245 
246  /* Clear interrupts */
247  ams_i2c_clear_irq(AMS_IRQ_ALL);
248 
249  printk(KERN_INFO "ams: Unloading\n");
250 
251  ams_info.has_device = 0;
252  }
253 
254  return 0;
255 }
256 
257 static void ams_i2c_exit(void)
258 {
259  i2c_del_driver(&ams_i2c_driver);
260 }
261 
263 {
264  int result;
265 
266  /* Set implementation stuff */
267  ams_info.of_node = np;
268  ams_info.exit = ams_i2c_exit;
269  ams_info.get_vendor = ams_i2c_get_vendor;
270  ams_info.get_xyz = ams_i2c_get_xyz;
271  ams_info.clear_irq = ams_i2c_clear_irq;
272  ams_info.bustype = BUS_I2C;
273 
274  result = i2c_add_driver(&ams_i2c_driver);
275 
276  return result;
277 }