~ubuntu-branches/debian/wheezy/linux-2.6/wheezy

« back to all changes in this revision

Viewing changes to drivers/hwmon/ams/ams-i2c.c

  • Committer: Bazaar Package Importer
  • Author(s): Ben Hutchings, Ben Hutchings, Aurelien Jarno, Martin Michlmayr
  • Date: 2011-04-06 13:53:30 UTC
  • mfrom: (43.1.5 sid)
  • Revision ID: james.westby@ubuntu.com-20110406135330-wjufxhd0tvn3zx4z
Tags: 2.6.38-3
[ Ben Hutchings ]
* [ppc64] Add to linux-tools package architectures (Closes: #620124)
* [amd64] Save cr4 to mmu_cr4_features at boot time (Closes: #620284)
* appletalk: Fix bugs introduced when removing use of BKL
* ALSA: Fix yet another race in disconnection
* cciss: Fix lost command issue
* ath9k: Fix kernel panic in AR2427
* ses: Avoid kernel panic when lun 0 is not mapped
* PCI/ACPI: Report ASPM support to BIOS if not disabled from command line

[ Aurelien Jarno ]
* rtlwifi: fix build when PCI is not enabled.

[ Martin Michlmayr ]
* rtlwifi: Eliminate udelay calls with too large values (Closes: #620204)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Apple Motion Sensor driver (I2C variant)
3
 
 *
4
 
 * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
5
 
 * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch)
6
 
 *
7
 
 * Clean room implementation based on the reverse engineered Mac OS X driver by
8
 
 * Johannes Berg <johannes@sipsolutions.net>, 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 */
51
 
enum ams_i2c_cmd {
52
 
        AMS_CMD_NOOP = 0,
53
 
        AMS_CMD_VERSION,
54
 
        AMS_CMD_READMEM,
55
 
        AMS_CMD_WRITEMEM,
56
 
        AMS_CMD_ERASEMEM,
57
 
        AMS_CMD_READEE,
58
 
        AMS_CMD_WRITEEE,
59
 
        AMS_CMD_RESET,
60
 
        AMS_CMD_START,
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
 
        { "ams", 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
 
 
106
 
                schedule_timeout_uninterruptible(HZ / 20);
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
 
 
190
 
        ams_i2c_cmd(AMS_CMD_READMEM);
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
 
 
200
 
        ams_i2c_cmd(AMS_CMD_VERSION);
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) {
241
 
                /* Disable interrupts */
242
 
                ams_i2c_set_irq(AMS_IRQ_ALL, 0);
243
 
 
244
 
                /* Clear interrupts */
245
 
                ams_i2c_clear_irq(AMS_IRQ_ALL);
246
 
 
247
 
                printk(KERN_INFO "ams: Unloading\n");
248
 
 
249
 
                ams_info.has_device = 0;
250
 
        }
251
 
 
252
 
        return 0;
253
 
}
254
 
 
255
 
static void ams_i2c_exit(void)
256
 
{
257
 
        i2c_del_driver(&ams_i2c_driver);
258
 
}
259
 
 
260
 
int __init ams_i2c_init(struct device_node *np)
261
 
{
262
 
        int result;
263
 
 
264
 
        /* Set implementation stuff */
265
 
        ams_info.of_node = np;
266
 
        ams_info.exit = ams_i2c_exit;
267
 
        ams_info.get_vendor = ams_i2c_get_vendor;
268
 
        ams_info.get_xyz = ams_i2c_get_xyz;
269
 
        ams_info.clear_irq = ams_i2c_clear_irq;
270
 
        ams_info.bustype = BUS_I2C;
271
 
 
272
 
        result = i2c_add_driver(&ams_i2c_driver);
273
 
 
274
 
        return result;
275
 
}