Commit bd5f47ec961594b1091839333600008f8166fd00
Committed by
Jean Delvare
1 parent
6dfee85397
Exists in
master
and in
4 other branches
Move ams driver to macintosh
The ams driver isn't a hardware monitoring driver, so it shouldn't live under driver/hwmon. drivers/macintosh seems much more appropriate, as the driver is only useful on PowerBooks and iBooks. Signed-off-by: Jean Delvare <khali@linux-fr.org> Cc: Guenter Roeck <guenter.roeck@ericsson.com> Cc: Stelian Pop <stelian@popies.net> Cc: Michael Hanselmann <linux-kernel@hansmi.ch> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Grant Likely <grant.likely@secretlab.ca>
Showing 17 changed files with 992 additions and 991 deletions Side-by-side Diff
- MAINTAINERS
- drivers/hwmon/Kconfig
- drivers/hwmon/Makefile
- drivers/hwmon/ams/Makefile
- drivers/hwmon/ams/ams-core.c
- drivers/hwmon/ams/ams-i2c.c
- drivers/hwmon/ams/ams-input.c
- drivers/hwmon/ams/ams-pmu.c
- drivers/hwmon/ams/ams.h
- drivers/macintosh/Kconfig
- drivers/macintosh/Makefile
- drivers/macintosh/ams/Makefile
- drivers/macintosh/ams/ams-core.c
- drivers/macintosh/ams/ams-i2c.c
- drivers/macintosh/ams/ams-input.c
- drivers/macintosh/ams/ams-pmu.c
- drivers/macintosh/ams/ams.h
MAINTAINERS
drivers/hwmon/Kconfig
... | ... | @@ -249,32 +249,6 @@ |
249 | 249 | This driver can also be built as a module. If so, the module |
250 | 250 | will be called k10temp. |
251 | 251 | |
252 | -config SENSORS_AMS | |
253 | - tristate "Apple Motion Sensor driver" | |
254 | - depends on PPC_PMAC && !PPC64 && INPUT && ((ADB_PMU && I2C = y) || (ADB_PMU && !I2C) || I2C) && EXPERIMENTAL | |
255 | - select INPUT_POLLDEV | |
256 | - help | |
257 | - Support for the motion sensor included in PowerBooks. Includes | |
258 | - implementations for PMU and I2C. | |
259 | - | |
260 | - This driver can also be built as a module. If so, the module | |
261 | - will be called ams. | |
262 | - | |
263 | -config SENSORS_AMS_PMU | |
264 | - bool "PMU variant" | |
265 | - depends on SENSORS_AMS && ADB_PMU | |
266 | - default y | |
267 | - help | |
268 | - PMU variant of motion sensor, found in late 2005 PowerBooks. | |
269 | - | |
270 | -config SENSORS_AMS_I2C | |
271 | - bool "I2C variant" | |
272 | - depends on SENSORS_AMS && I2C | |
273 | - default y | |
274 | - help | |
275 | - I2C variant of motion sensor, found in early 2005 PowerBooks and | |
276 | - iBooks. | |
277 | - | |
278 | 252 | config SENSORS_ASB100 |
279 | 253 | tristate "Asus ASB100 Bach" |
280 | 254 | depends on X86 && I2C && EXPERIMENTAL |
drivers/hwmon/Makefile
... | ... | @@ -36,7 +36,6 @@ |
36 | 36 | obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o |
37 | 37 | obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o |
38 | 38 | obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o |
39 | -obj-$(CONFIG_SENSORS_AMS) += ams/ | |
40 | 39 | obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o |
41 | 40 | obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o |
42 | 41 | obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o |
drivers/hwmon/ams/Makefile
drivers/hwmon/ams/ams-core.c
1 | -/* | |
2 | - * Apple Motion Sensor driver | |
3 | - * | |
4 | - * Copyright (C) 2005 Stelian Pop (stelian@popies.net) | |
5 | - * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch) | |
6 | - * | |
7 | - * This program is free software; you can redistribute it and/or modify | |
8 | - * it under the terms of the GNU General Public License as published by | |
9 | - * the Free Software Foundation; either version 2 of the License, or | |
10 | - * (at your option) any later version. | |
11 | - * | |
12 | - * This program is distributed in the hope that it will be useful, | |
13 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | - * GNU General Public License for more details. | |
16 | - * | |
17 | - * You should have received a copy of the GNU General Public License | |
18 | - * along with this program; if not, write to the Free Software | |
19 | - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
20 | - */ | |
21 | - | |
22 | -#include <linux/module.h> | |
23 | -#include <linux/types.h> | |
24 | -#include <linux/errno.h> | |
25 | -#include <linux/init.h> | |
26 | -#include <linux/of_platform.h> | |
27 | -#include <asm/pmac_pfunc.h> | |
28 | - | |
29 | -#include "ams.h" | |
30 | - | |
31 | -/* There is only one motion sensor per machine */ | |
32 | -struct ams ams_info; | |
33 | - | |
34 | -static unsigned int verbose; | |
35 | -module_param(verbose, bool, 0644); | |
36 | -MODULE_PARM_DESC(verbose, "Show free falls and shocks in kernel output"); | |
37 | - | |
38 | -/* Call with ams_info.lock held! */ | |
39 | -void ams_sensors(s8 *x, s8 *y, s8 *z) | |
40 | -{ | |
41 | - u32 orient = ams_info.vflag? ams_info.orient1 : ams_info.orient2; | |
42 | - | |
43 | - if (orient & 0x80) | |
44 | - /* X and Y swapped */ | |
45 | - ams_info.get_xyz(y, x, z); | |
46 | - else | |
47 | - ams_info.get_xyz(x, y, z); | |
48 | - | |
49 | - if (orient & 0x04) | |
50 | - *z = ~(*z); | |
51 | - if (orient & 0x02) | |
52 | - *y = ~(*y); | |
53 | - if (orient & 0x01) | |
54 | - *x = ~(*x); | |
55 | -} | |
56 | - | |
57 | -static ssize_t ams_show_current(struct device *dev, | |
58 | - struct device_attribute *attr, char *buf) | |
59 | -{ | |
60 | - s8 x, y, z; | |
61 | - | |
62 | - mutex_lock(&ams_info.lock); | |
63 | - ams_sensors(&x, &y, &z); | |
64 | - mutex_unlock(&ams_info.lock); | |
65 | - | |
66 | - return snprintf(buf, PAGE_SIZE, "%d %d %d\n", x, y, z); | |
67 | -} | |
68 | - | |
69 | -static DEVICE_ATTR(current, S_IRUGO, ams_show_current, NULL); | |
70 | - | |
71 | -static void ams_handle_irq(void *data) | |
72 | -{ | |
73 | - enum ams_irq irq = *((enum ams_irq *)data); | |
74 | - | |
75 | - spin_lock(&ams_info.irq_lock); | |
76 | - | |
77 | - ams_info.worker_irqs |= irq; | |
78 | - schedule_work(&ams_info.worker); | |
79 | - | |
80 | - spin_unlock(&ams_info.irq_lock); | |
81 | -} | |
82 | - | |
83 | -static enum ams_irq ams_freefall_irq_data = AMS_IRQ_FREEFALL; | |
84 | -static struct pmf_irq_client ams_freefall_client = { | |
85 | - .owner = THIS_MODULE, | |
86 | - .handler = ams_handle_irq, | |
87 | - .data = &ams_freefall_irq_data, | |
88 | -}; | |
89 | - | |
90 | -static enum ams_irq ams_shock_irq_data = AMS_IRQ_SHOCK; | |
91 | -static struct pmf_irq_client ams_shock_client = { | |
92 | - .owner = THIS_MODULE, | |
93 | - .handler = ams_handle_irq, | |
94 | - .data = &ams_shock_irq_data, | |
95 | -}; | |
96 | - | |
97 | -/* Once hard disk parking is implemented in the kernel, this function can | |
98 | - * trigger it. | |
99 | - */ | |
100 | -static void ams_worker(struct work_struct *work) | |
101 | -{ | |
102 | - unsigned long flags; | |
103 | - u8 irqs_to_clear; | |
104 | - | |
105 | - mutex_lock(&ams_info.lock); | |
106 | - | |
107 | - spin_lock_irqsave(&ams_info.irq_lock, flags); | |
108 | - irqs_to_clear = ams_info.worker_irqs; | |
109 | - | |
110 | - if (ams_info.worker_irqs & AMS_IRQ_FREEFALL) { | |
111 | - if (verbose) | |
112 | - printk(KERN_INFO "ams: freefall detected!\n"); | |
113 | - | |
114 | - ams_info.worker_irqs &= ~AMS_IRQ_FREEFALL; | |
115 | - } | |
116 | - | |
117 | - if (ams_info.worker_irqs & AMS_IRQ_SHOCK) { | |
118 | - if (verbose) | |
119 | - printk(KERN_INFO "ams: shock detected!\n"); | |
120 | - | |
121 | - ams_info.worker_irqs &= ~AMS_IRQ_SHOCK; | |
122 | - } | |
123 | - | |
124 | - spin_unlock_irqrestore(&ams_info.irq_lock, flags); | |
125 | - | |
126 | - ams_info.clear_irq(irqs_to_clear); | |
127 | - | |
128 | - mutex_unlock(&ams_info.lock); | |
129 | -} | |
130 | - | |
131 | -/* Call with ams_info.lock held! */ | |
132 | -int ams_sensor_attach(void) | |
133 | -{ | |
134 | - int result; | |
135 | - const u32 *prop; | |
136 | - | |
137 | - /* Get orientation */ | |
138 | - prop = of_get_property(ams_info.of_node, "orientation", NULL); | |
139 | - if (!prop) | |
140 | - return -ENODEV; | |
141 | - ams_info.orient1 = *prop; | |
142 | - ams_info.orient2 = *(prop + 1); | |
143 | - | |
144 | - /* Register freefall interrupt handler */ | |
145 | - result = pmf_register_irq_client(ams_info.of_node, | |
146 | - "accel-int-1", | |
147 | - &ams_freefall_client); | |
148 | - if (result < 0) | |
149 | - return -ENODEV; | |
150 | - | |
151 | - /* Reset saved irqs */ | |
152 | - ams_info.worker_irqs = 0; | |
153 | - | |
154 | - /* Register shock interrupt handler */ | |
155 | - result = pmf_register_irq_client(ams_info.of_node, | |
156 | - "accel-int-2", | |
157 | - &ams_shock_client); | |
158 | - if (result < 0) | |
159 | - goto release_freefall; | |
160 | - | |
161 | - /* Create device */ | |
162 | - ams_info.of_dev = of_platform_device_create(ams_info.of_node, "ams", NULL); | |
163 | - if (!ams_info.of_dev) { | |
164 | - result = -ENODEV; | |
165 | - goto release_shock; | |
166 | - } | |
167 | - | |
168 | - /* Create attributes */ | |
169 | - result = device_create_file(&ams_info.of_dev->dev, &dev_attr_current); | |
170 | - if (result) | |
171 | - goto release_of; | |
172 | - | |
173 | - ams_info.vflag = !!(ams_info.get_vendor() & 0x10); | |
174 | - | |
175 | - /* Init input device */ | |
176 | - result = ams_input_init(); | |
177 | - if (result) | |
178 | - goto release_device_file; | |
179 | - | |
180 | - return result; | |
181 | -release_device_file: | |
182 | - device_remove_file(&ams_info.of_dev->dev, &dev_attr_current); | |
183 | -release_of: | |
184 | - of_device_unregister(ams_info.of_dev); | |
185 | -release_shock: | |
186 | - pmf_unregister_irq_client(&ams_shock_client); | |
187 | -release_freefall: | |
188 | - pmf_unregister_irq_client(&ams_freefall_client); | |
189 | - return result; | |
190 | -} | |
191 | - | |
192 | -int __init ams_init(void) | |
193 | -{ | |
194 | - struct device_node *np; | |
195 | - | |
196 | - spin_lock_init(&ams_info.irq_lock); | |
197 | - mutex_init(&ams_info.lock); | |
198 | - INIT_WORK(&ams_info.worker, ams_worker); | |
199 | - | |
200 | -#ifdef CONFIG_SENSORS_AMS_I2C | |
201 | - np = of_find_node_by_name(NULL, "accelerometer"); | |
202 | - if (np && of_device_is_compatible(np, "AAPL,accelerometer_1")) | |
203 | - /* Found I2C motion sensor */ | |
204 | - return ams_i2c_init(np); | |
205 | -#endif | |
206 | - | |
207 | -#ifdef CONFIG_SENSORS_AMS_PMU | |
208 | - np = of_find_node_by_name(NULL, "sms"); | |
209 | - if (np && of_device_is_compatible(np, "sms")) | |
210 | - /* Found PMU motion sensor */ | |
211 | - return ams_pmu_init(np); | |
212 | -#endif | |
213 | - return -ENODEV; | |
214 | -} | |
215 | - | |
216 | -void ams_sensor_detach(void) | |
217 | -{ | |
218 | - /* Remove input device */ | |
219 | - ams_input_exit(); | |
220 | - | |
221 | - /* Remove attributes */ | |
222 | - device_remove_file(&ams_info.of_dev->dev, &dev_attr_current); | |
223 | - | |
224 | - /* Flush interrupt worker | |
225 | - * | |
226 | - * We do this after ams_info.exit(), because an interrupt might | |
227 | - * have arrived before disabling them. | |
228 | - */ | |
229 | - flush_scheduled_work(); | |
230 | - | |
231 | - /* Remove device */ | |
232 | - of_device_unregister(ams_info.of_dev); | |
233 | - | |
234 | - /* Remove handler */ | |
235 | - pmf_unregister_irq_client(&ams_shock_client); | |
236 | - pmf_unregister_irq_client(&ams_freefall_client); | |
237 | -} | |
238 | - | |
239 | -static void __exit ams_exit(void) | |
240 | -{ | |
241 | - /* Shut down implementation */ | |
242 | - ams_info.exit(); | |
243 | -} | |
244 | - | |
245 | -MODULE_AUTHOR("Stelian Pop, Michael Hanselmann"); | |
246 | -MODULE_DESCRIPTION("Apple Motion Sensor driver"); | |
247 | -MODULE_LICENSE("GPL"); | |
248 | - | |
249 | -module_init(ams_init); | |
250 | -module_exit(ams_exit); |
drivers/hwmon/ams/ams-i2c.c
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 | - ams_sensor_detach(); | |
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 | - | |
262 | -int __init ams_i2c_init(struct device_node *np) | |
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 | -} |
drivers/hwmon/ams/ams-input.c
1 | -/* | |
2 | - * Apple Motion Sensor driver (joystick emulation) | |
3 | - * | |
4 | - * Copyright (C) 2005 Stelian Pop (stelian@popies.net) | |
5 | - * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch) | |
6 | - * | |
7 | - * This program is free software; you can redistribute it and/or modify | |
8 | - * it under the terms of the GNU General Public License as published by | |
9 | - * the Free Software Foundation; either version 2 of the License, or | |
10 | - * (at your option) any later version. | |
11 | - */ | |
12 | - | |
13 | -#include <linux/module.h> | |
14 | - | |
15 | -#include <linux/types.h> | |
16 | -#include <linux/errno.h> | |
17 | -#include <linux/init.h> | |
18 | -#include <linux/delay.h> | |
19 | - | |
20 | -#include "ams.h" | |
21 | - | |
22 | -static unsigned int joystick; | |
23 | -module_param(joystick, bool, S_IRUGO); | |
24 | -MODULE_PARM_DESC(joystick, "Enable the input class device on module load"); | |
25 | - | |
26 | -static unsigned int invert; | |
27 | -module_param(invert, bool, S_IWUSR | S_IRUGO); | |
28 | -MODULE_PARM_DESC(invert, "Invert input data on X and Y axis"); | |
29 | - | |
30 | -static DEFINE_MUTEX(ams_input_mutex); | |
31 | - | |
32 | -static void ams_idev_poll(struct input_polled_dev *dev) | |
33 | -{ | |
34 | - struct input_dev *idev = dev->input; | |
35 | - s8 x, y, z; | |
36 | - | |
37 | - mutex_lock(&ams_info.lock); | |
38 | - | |
39 | - ams_sensors(&x, &y, &z); | |
40 | - | |
41 | - x -= ams_info.xcalib; | |
42 | - y -= ams_info.ycalib; | |
43 | - z -= ams_info.zcalib; | |
44 | - | |
45 | - input_report_abs(idev, ABS_X, invert ? -x : x); | |
46 | - input_report_abs(idev, ABS_Y, invert ? -y : y); | |
47 | - input_report_abs(idev, ABS_Z, z); | |
48 | - | |
49 | - input_sync(idev); | |
50 | - | |
51 | - mutex_unlock(&ams_info.lock); | |
52 | -} | |
53 | - | |
54 | -/* Call with ams_info.lock held! */ | |
55 | -static int ams_input_enable(void) | |
56 | -{ | |
57 | - struct input_dev *input; | |
58 | - s8 x, y, z; | |
59 | - int error; | |
60 | - | |
61 | - ams_sensors(&x, &y, &z); | |
62 | - ams_info.xcalib = x; | |
63 | - ams_info.ycalib = y; | |
64 | - ams_info.zcalib = z; | |
65 | - | |
66 | - ams_info.idev = input_allocate_polled_device(); | |
67 | - if (!ams_info.idev) | |
68 | - return -ENOMEM; | |
69 | - | |
70 | - ams_info.idev->poll = ams_idev_poll; | |
71 | - ams_info.idev->poll_interval = 25; | |
72 | - | |
73 | - input = ams_info.idev->input; | |
74 | - input->name = "Apple Motion Sensor"; | |
75 | - input->id.bustype = ams_info.bustype; | |
76 | - input->id.vendor = 0; | |
77 | - input->dev.parent = &ams_info.of_dev->dev; | |
78 | - | |
79 | - input_set_abs_params(input, ABS_X, -50, 50, 3, 0); | |
80 | - input_set_abs_params(input, ABS_Y, -50, 50, 3, 0); | |
81 | - input_set_abs_params(input, ABS_Z, -50, 50, 3, 0); | |
82 | - | |
83 | - set_bit(EV_ABS, input->evbit); | |
84 | - set_bit(EV_KEY, input->evbit); | |
85 | - set_bit(BTN_TOUCH, input->keybit); | |
86 | - | |
87 | - error = input_register_polled_device(ams_info.idev); | |
88 | - if (error) { | |
89 | - input_free_polled_device(ams_info.idev); | |
90 | - ams_info.idev = NULL; | |
91 | - return error; | |
92 | - } | |
93 | - | |
94 | - joystick = 1; | |
95 | - | |
96 | - return 0; | |
97 | -} | |
98 | - | |
99 | -static void ams_input_disable(void) | |
100 | -{ | |
101 | - if (ams_info.idev) { | |
102 | - input_unregister_polled_device(ams_info.idev); | |
103 | - input_free_polled_device(ams_info.idev); | |
104 | - ams_info.idev = NULL; | |
105 | - } | |
106 | - | |
107 | - joystick = 0; | |
108 | -} | |
109 | - | |
110 | -static ssize_t ams_input_show_joystick(struct device *dev, | |
111 | - struct device_attribute *attr, char *buf) | |
112 | -{ | |
113 | - return sprintf(buf, "%d\n", joystick); | |
114 | -} | |
115 | - | |
116 | -static ssize_t ams_input_store_joystick(struct device *dev, | |
117 | - struct device_attribute *attr, const char *buf, size_t count) | |
118 | -{ | |
119 | - unsigned long enable; | |
120 | - int error = 0; | |
121 | - | |
122 | - if (strict_strtoul(buf, 0, &enable) || enable > 1) | |
123 | - return -EINVAL; | |
124 | - | |
125 | - mutex_lock(&ams_input_mutex); | |
126 | - | |
127 | - if (enable != joystick) { | |
128 | - if (enable) | |
129 | - error = ams_input_enable(); | |
130 | - else | |
131 | - ams_input_disable(); | |
132 | - } | |
133 | - | |
134 | - mutex_unlock(&ams_input_mutex); | |
135 | - | |
136 | - return error ? error : count; | |
137 | -} | |
138 | - | |
139 | -static DEVICE_ATTR(joystick, S_IRUGO | S_IWUSR, | |
140 | - ams_input_show_joystick, ams_input_store_joystick); | |
141 | - | |
142 | -int ams_input_init(void) | |
143 | -{ | |
144 | - if (joystick) | |
145 | - ams_input_enable(); | |
146 | - | |
147 | - return device_create_file(&ams_info.of_dev->dev, &dev_attr_joystick); | |
148 | -} | |
149 | - | |
150 | -void ams_input_exit(void) | |
151 | -{ | |
152 | - device_remove_file(&ams_info.of_dev->dev, &dev_attr_joystick); | |
153 | - | |
154 | - mutex_lock(&ams_input_mutex); | |
155 | - ams_input_disable(); | |
156 | - mutex_unlock(&ams_input_mutex); | |
157 | -} |
drivers/hwmon/ams/ams-pmu.c
1 | -/* | |
2 | - * Apple Motion Sensor driver (PMU variant) | |
3 | - * | |
4 | - * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch) | |
5 | - * | |
6 | - * This program is free software; you can redistribute it and/or modify | |
7 | - * it under the terms of the GNU General Public License as published by | |
8 | - * the Free Software Foundation; either version 2 of the License, or | |
9 | - * (at your option) any later version. | |
10 | - */ | |
11 | - | |
12 | -#include <linux/module.h> | |
13 | -#include <linux/types.h> | |
14 | -#include <linux/errno.h> | |
15 | -#include <linux/init.h> | |
16 | -#include <linux/adb.h> | |
17 | -#include <linux/pmu.h> | |
18 | - | |
19 | -#include "ams.h" | |
20 | - | |
21 | -/* Attitude */ | |
22 | -#define AMS_X 0x00 | |
23 | -#define AMS_Y 0x01 | |
24 | -#define AMS_Z 0x02 | |
25 | - | |
26 | -/* Not exactly known, maybe chip vendor */ | |
27 | -#define AMS_VENDOR 0x03 | |
28 | - | |
29 | -/* Freefall registers */ | |
30 | -#define AMS_FF_CLEAR 0x04 | |
31 | -#define AMS_FF_ENABLE 0x05 | |
32 | -#define AMS_FF_LOW_LIMIT 0x06 | |
33 | -#define AMS_FF_DEBOUNCE 0x07 | |
34 | - | |
35 | -/* Shock registers */ | |
36 | -#define AMS_SHOCK_CLEAR 0x08 | |
37 | -#define AMS_SHOCK_ENABLE 0x09 | |
38 | -#define AMS_SHOCK_HIGH_LIMIT 0x0a | |
39 | -#define AMS_SHOCK_DEBOUNCE 0x0b | |
40 | - | |
41 | -/* Global interrupt and power control register */ | |
42 | -#define AMS_CONTROL 0x0c | |
43 | - | |
44 | -static u8 ams_pmu_cmd; | |
45 | - | |
46 | -static void ams_pmu_req_complete(struct adb_request *req) | |
47 | -{ | |
48 | - complete((struct completion *)req->arg); | |
49 | -} | |
50 | - | |
51 | -/* Only call this function from task context */ | |
52 | -static void ams_pmu_set_register(u8 reg, u8 value) | |
53 | -{ | |
54 | - static struct adb_request req; | |
55 | - DECLARE_COMPLETION(req_complete); | |
56 | - | |
57 | - req.arg = &req_complete; | |
58 | - if (pmu_request(&req, ams_pmu_req_complete, 4, ams_pmu_cmd, 0x00, reg, value)) | |
59 | - return; | |
60 | - | |
61 | - wait_for_completion(&req_complete); | |
62 | -} | |
63 | - | |
64 | -/* Only call this function from task context */ | |
65 | -static u8 ams_pmu_get_register(u8 reg) | |
66 | -{ | |
67 | - static struct adb_request req; | |
68 | - DECLARE_COMPLETION(req_complete); | |
69 | - | |
70 | - req.arg = &req_complete; | |
71 | - if (pmu_request(&req, ams_pmu_req_complete, 3, ams_pmu_cmd, 0x01, reg)) | |
72 | - return 0; | |
73 | - | |
74 | - wait_for_completion(&req_complete); | |
75 | - | |
76 | - if (req.reply_len > 0) | |
77 | - return req.reply[0]; | |
78 | - else | |
79 | - return 0; | |
80 | -} | |
81 | - | |
82 | -/* Enables or disables the specified interrupts */ | |
83 | -static void ams_pmu_set_irq(enum ams_irq reg, char enable) | |
84 | -{ | |
85 | - if (reg & AMS_IRQ_FREEFALL) { | |
86 | - u8 val = ams_pmu_get_register(AMS_FF_ENABLE); | |
87 | - if (enable) | |
88 | - val |= 0x80; | |
89 | - else | |
90 | - val &= ~0x80; | |
91 | - ams_pmu_set_register(AMS_FF_ENABLE, val); | |
92 | - } | |
93 | - | |
94 | - if (reg & AMS_IRQ_SHOCK) { | |
95 | - u8 val = ams_pmu_get_register(AMS_SHOCK_ENABLE); | |
96 | - if (enable) | |
97 | - val |= 0x80; | |
98 | - else | |
99 | - val &= ~0x80; | |
100 | - ams_pmu_set_register(AMS_SHOCK_ENABLE, val); | |
101 | - } | |
102 | - | |
103 | - if (reg & AMS_IRQ_GLOBAL) { | |
104 | - u8 val = ams_pmu_get_register(AMS_CONTROL); | |
105 | - if (enable) | |
106 | - val |= 0x80; | |
107 | - else | |
108 | - val &= ~0x80; | |
109 | - ams_pmu_set_register(AMS_CONTROL, val); | |
110 | - } | |
111 | -} | |
112 | - | |
113 | -static void ams_pmu_clear_irq(enum ams_irq reg) | |
114 | -{ | |
115 | - if (reg & AMS_IRQ_FREEFALL) | |
116 | - ams_pmu_set_register(AMS_FF_CLEAR, 0x00); | |
117 | - | |
118 | - if (reg & AMS_IRQ_SHOCK) | |
119 | - ams_pmu_set_register(AMS_SHOCK_CLEAR, 0x00); | |
120 | -} | |
121 | - | |
122 | -static u8 ams_pmu_get_vendor(void) | |
123 | -{ | |
124 | - return ams_pmu_get_register(AMS_VENDOR); | |
125 | -} | |
126 | - | |
127 | -static void ams_pmu_get_xyz(s8 *x, s8 *y, s8 *z) | |
128 | -{ | |
129 | - *x = ams_pmu_get_register(AMS_X); | |
130 | - *y = ams_pmu_get_register(AMS_Y); | |
131 | - *z = ams_pmu_get_register(AMS_Z); | |
132 | -} | |
133 | - | |
134 | -static void ams_pmu_exit(void) | |
135 | -{ | |
136 | - ams_sensor_detach(); | |
137 | - | |
138 | - /* Disable interrupts */ | |
139 | - ams_pmu_set_irq(AMS_IRQ_ALL, 0); | |
140 | - | |
141 | - /* Clear interrupts */ | |
142 | - ams_pmu_clear_irq(AMS_IRQ_ALL); | |
143 | - | |
144 | - ams_info.has_device = 0; | |
145 | - | |
146 | - printk(KERN_INFO "ams: Unloading\n"); | |
147 | -} | |
148 | - | |
149 | -int __init ams_pmu_init(struct device_node *np) | |
150 | -{ | |
151 | - const u32 *prop; | |
152 | - int result; | |
153 | - | |
154 | - /* Set implementation stuff */ | |
155 | - ams_info.of_node = np; | |
156 | - ams_info.exit = ams_pmu_exit; | |
157 | - ams_info.get_vendor = ams_pmu_get_vendor; | |
158 | - ams_info.get_xyz = ams_pmu_get_xyz; | |
159 | - ams_info.clear_irq = ams_pmu_clear_irq; | |
160 | - ams_info.bustype = BUS_HOST; | |
161 | - | |
162 | - /* Get PMU command, should be 0x4e, but we can never know */ | |
163 | - prop = of_get_property(ams_info.of_node, "reg", NULL); | |
164 | - if (!prop) | |
165 | - return -ENODEV; | |
166 | - | |
167 | - ams_pmu_cmd = ((*prop) >> 8) & 0xff; | |
168 | - | |
169 | - /* Disable interrupts */ | |
170 | - ams_pmu_set_irq(AMS_IRQ_ALL, 0); | |
171 | - | |
172 | - /* Clear interrupts */ | |
173 | - ams_pmu_clear_irq(AMS_IRQ_ALL); | |
174 | - | |
175 | - result = ams_sensor_attach(); | |
176 | - if (result < 0) | |
177 | - return result; | |
178 | - | |
179 | - /* Set default values */ | |
180 | - ams_pmu_set_register(AMS_FF_LOW_LIMIT, 0x15); | |
181 | - ams_pmu_set_register(AMS_FF_ENABLE, 0x08); | |
182 | - ams_pmu_set_register(AMS_FF_DEBOUNCE, 0x14); | |
183 | - | |
184 | - ams_pmu_set_register(AMS_SHOCK_HIGH_LIMIT, 0x60); | |
185 | - ams_pmu_set_register(AMS_SHOCK_ENABLE, 0x0f); | |
186 | - ams_pmu_set_register(AMS_SHOCK_DEBOUNCE, 0x14); | |
187 | - | |
188 | - ams_pmu_set_register(AMS_CONTROL, 0x4f); | |
189 | - | |
190 | - /* Clear interrupts */ | |
191 | - ams_pmu_clear_irq(AMS_IRQ_ALL); | |
192 | - | |
193 | - ams_info.has_device = 1; | |
194 | - | |
195 | - /* Enable interrupts */ | |
196 | - ams_pmu_set_irq(AMS_IRQ_ALL, 1); | |
197 | - | |
198 | - printk(KERN_INFO "ams: Found PMU based motion sensor\n"); | |
199 | - | |
200 | - return 0; | |
201 | -} |
drivers/hwmon/ams/ams.h
1 | -#include <linux/i2c.h> | |
2 | -#include <linux/input-polldev.h> | |
3 | -#include <linux/kthread.h> | |
4 | -#include <linux/mutex.h> | |
5 | -#include <linux/spinlock.h> | |
6 | -#include <linux/types.h> | |
7 | -#include <linux/of_device.h> | |
8 | - | |
9 | -enum ams_irq { | |
10 | - AMS_IRQ_FREEFALL = 0x01, | |
11 | - AMS_IRQ_SHOCK = 0x02, | |
12 | - AMS_IRQ_GLOBAL = 0x04, | |
13 | - AMS_IRQ_ALL = | |
14 | - AMS_IRQ_FREEFALL | | |
15 | - AMS_IRQ_SHOCK | | |
16 | - AMS_IRQ_GLOBAL, | |
17 | -}; | |
18 | - | |
19 | -struct ams { | |
20 | - /* Locks */ | |
21 | - spinlock_t irq_lock; | |
22 | - struct mutex lock; | |
23 | - | |
24 | - /* General properties */ | |
25 | - struct device_node *of_node; | |
26 | - struct platform_device *of_dev; | |
27 | - char has_device; | |
28 | - char vflag; | |
29 | - u32 orient1; | |
30 | - u32 orient2; | |
31 | - | |
32 | - /* Interrupt worker */ | |
33 | - struct work_struct worker; | |
34 | - u8 worker_irqs; | |
35 | - | |
36 | - /* Implementation | |
37 | - * | |
38 | - * Only call these functions with the main lock held. | |
39 | - */ | |
40 | - void (*exit)(void); | |
41 | - | |
42 | - void (*get_xyz)(s8 *x, s8 *y, s8 *z); | |
43 | - u8 (*get_vendor)(void); | |
44 | - | |
45 | - void (*clear_irq)(enum ams_irq reg); | |
46 | - | |
47 | -#ifdef CONFIG_SENSORS_AMS_I2C | |
48 | - /* I2C properties */ | |
49 | - struct i2c_client *i2c_client; | |
50 | -#endif | |
51 | - | |
52 | - /* Joystick emulation */ | |
53 | - struct input_polled_dev *idev; | |
54 | - __u16 bustype; | |
55 | - | |
56 | - /* calibrated null values */ | |
57 | - int xcalib, ycalib, zcalib; | |
58 | -}; | |
59 | - | |
60 | -extern struct ams ams_info; | |
61 | - | |
62 | -extern void ams_sensors(s8 *x, s8 *y, s8 *z); | |
63 | -extern int ams_sensor_attach(void); | |
64 | -extern void ams_sensor_detach(void); | |
65 | - | |
66 | -extern int ams_pmu_init(struct device_node *np); | |
67 | -extern int ams_i2c_init(struct device_node *np); | |
68 | - | |
69 | -extern int ams_input_init(void); | |
70 | -extern void ams_input_exit(void); |
drivers/macintosh/Kconfig
... | ... | @@ -256,5 +256,31 @@ |
256 | 256 | This driver provides some support to control the front panel |
257 | 257 | blue LEDs "vu-meter" of the XServer macs. |
258 | 258 | |
259 | +config SENSORS_AMS | |
260 | + tristate "Apple Motion Sensor driver" | |
261 | + depends on PPC_PMAC && !PPC64 && INPUT && ((ADB_PMU && I2C = y) || (ADB_PMU && !I2C) || I2C) && EXPERIMENTAL | |
262 | + select INPUT_POLLDEV | |
263 | + help | |
264 | + Support for the motion sensor included in PowerBooks. Includes | |
265 | + implementations for PMU and I2C. | |
266 | + | |
267 | + This driver can also be built as a module. If so, the module | |
268 | + will be called ams. | |
269 | + | |
270 | +config SENSORS_AMS_PMU | |
271 | + bool "PMU variant" | |
272 | + depends on SENSORS_AMS && ADB_PMU | |
273 | + default y | |
274 | + help | |
275 | + PMU variant of motion sensor, found in late 2005 PowerBooks. | |
276 | + | |
277 | +config SENSORS_AMS_I2C | |
278 | + bool "I2C variant" | |
279 | + depends on SENSORS_AMS && I2C | |
280 | + default y | |
281 | + help | |
282 | + I2C variant of motion sensor, found in early 2005 PowerBooks and | |
283 | + iBooks. | |
284 | + | |
259 | 285 | endif # MACINTOSH_DRIVERS |
drivers/macintosh/Makefile
drivers/macintosh/ams/Makefile
drivers/macintosh/ams/ams-core.c
1 | +/* | |
2 | + * Apple Motion Sensor driver | |
3 | + * | |
4 | + * Copyright (C) 2005 Stelian Pop (stelian@popies.net) | |
5 | + * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch) | |
6 | + * | |
7 | + * This program is free software; you can redistribute it and/or modify | |
8 | + * it under the terms of the GNU General Public License as published by | |
9 | + * the Free Software Foundation; either version 2 of the License, or | |
10 | + * (at your option) any later version. | |
11 | + * | |
12 | + * This program is distributed in the hope that it will be useful, | |
13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | + * GNU General Public License for more details. | |
16 | + * | |
17 | + * You should have received a copy of the GNU General Public License | |
18 | + * along with this program; if not, write to the Free Software | |
19 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
20 | + */ | |
21 | + | |
22 | +#include <linux/module.h> | |
23 | +#include <linux/types.h> | |
24 | +#include <linux/errno.h> | |
25 | +#include <linux/init.h> | |
26 | +#include <linux/of_platform.h> | |
27 | +#include <asm/pmac_pfunc.h> | |
28 | + | |
29 | +#include "ams.h" | |
30 | + | |
31 | +/* There is only one motion sensor per machine */ | |
32 | +struct ams ams_info; | |
33 | + | |
34 | +static unsigned int verbose; | |
35 | +module_param(verbose, bool, 0644); | |
36 | +MODULE_PARM_DESC(verbose, "Show free falls and shocks in kernel output"); | |
37 | + | |
38 | +/* Call with ams_info.lock held! */ | |
39 | +void ams_sensors(s8 *x, s8 *y, s8 *z) | |
40 | +{ | |
41 | + u32 orient = ams_info.vflag? ams_info.orient1 : ams_info.orient2; | |
42 | + | |
43 | + if (orient & 0x80) | |
44 | + /* X and Y swapped */ | |
45 | + ams_info.get_xyz(y, x, z); | |
46 | + else | |
47 | + ams_info.get_xyz(x, y, z); | |
48 | + | |
49 | + if (orient & 0x04) | |
50 | + *z = ~(*z); | |
51 | + if (orient & 0x02) | |
52 | + *y = ~(*y); | |
53 | + if (orient & 0x01) | |
54 | + *x = ~(*x); | |
55 | +} | |
56 | + | |
57 | +static ssize_t ams_show_current(struct device *dev, | |
58 | + struct device_attribute *attr, char *buf) | |
59 | +{ | |
60 | + s8 x, y, z; | |
61 | + | |
62 | + mutex_lock(&ams_info.lock); | |
63 | + ams_sensors(&x, &y, &z); | |
64 | + mutex_unlock(&ams_info.lock); | |
65 | + | |
66 | + return snprintf(buf, PAGE_SIZE, "%d %d %d\n", x, y, z); | |
67 | +} | |
68 | + | |
69 | +static DEVICE_ATTR(current, S_IRUGO, ams_show_current, NULL); | |
70 | + | |
71 | +static void ams_handle_irq(void *data) | |
72 | +{ | |
73 | + enum ams_irq irq = *((enum ams_irq *)data); | |
74 | + | |
75 | + spin_lock(&ams_info.irq_lock); | |
76 | + | |
77 | + ams_info.worker_irqs |= irq; | |
78 | + schedule_work(&ams_info.worker); | |
79 | + | |
80 | + spin_unlock(&ams_info.irq_lock); | |
81 | +} | |
82 | + | |
83 | +static enum ams_irq ams_freefall_irq_data = AMS_IRQ_FREEFALL; | |
84 | +static struct pmf_irq_client ams_freefall_client = { | |
85 | + .owner = THIS_MODULE, | |
86 | + .handler = ams_handle_irq, | |
87 | + .data = &ams_freefall_irq_data, | |
88 | +}; | |
89 | + | |
90 | +static enum ams_irq ams_shock_irq_data = AMS_IRQ_SHOCK; | |
91 | +static struct pmf_irq_client ams_shock_client = { | |
92 | + .owner = THIS_MODULE, | |
93 | + .handler = ams_handle_irq, | |
94 | + .data = &ams_shock_irq_data, | |
95 | +}; | |
96 | + | |
97 | +/* Once hard disk parking is implemented in the kernel, this function can | |
98 | + * trigger it. | |
99 | + */ | |
100 | +static void ams_worker(struct work_struct *work) | |
101 | +{ | |
102 | + unsigned long flags; | |
103 | + u8 irqs_to_clear; | |
104 | + | |
105 | + mutex_lock(&ams_info.lock); | |
106 | + | |
107 | + spin_lock_irqsave(&ams_info.irq_lock, flags); | |
108 | + irqs_to_clear = ams_info.worker_irqs; | |
109 | + | |
110 | + if (ams_info.worker_irqs & AMS_IRQ_FREEFALL) { | |
111 | + if (verbose) | |
112 | + printk(KERN_INFO "ams: freefall detected!\n"); | |
113 | + | |
114 | + ams_info.worker_irqs &= ~AMS_IRQ_FREEFALL; | |
115 | + } | |
116 | + | |
117 | + if (ams_info.worker_irqs & AMS_IRQ_SHOCK) { | |
118 | + if (verbose) | |
119 | + printk(KERN_INFO "ams: shock detected!\n"); | |
120 | + | |
121 | + ams_info.worker_irqs &= ~AMS_IRQ_SHOCK; | |
122 | + } | |
123 | + | |
124 | + spin_unlock_irqrestore(&ams_info.irq_lock, flags); | |
125 | + | |
126 | + ams_info.clear_irq(irqs_to_clear); | |
127 | + | |
128 | + mutex_unlock(&ams_info.lock); | |
129 | +} | |
130 | + | |
131 | +/* Call with ams_info.lock held! */ | |
132 | +int ams_sensor_attach(void) | |
133 | +{ | |
134 | + int result; | |
135 | + const u32 *prop; | |
136 | + | |
137 | + /* Get orientation */ | |
138 | + prop = of_get_property(ams_info.of_node, "orientation", NULL); | |
139 | + if (!prop) | |
140 | + return -ENODEV; | |
141 | + ams_info.orient1 = *prop; | |
142 | + ams_info.orient2 = *(prop + 1); | |
143 | + | |
144 | + /* Register freefall interrupt handler */ | |
145 | + result = pmf_register_irq_client(ams_info.of_node, | |
146 | + "accel-int-1", | |
147 | + &ams_freefall_client); | |
148 | + if (result < 0) | |
149 | + return -ENODEV; | |
150 | + | |
151 | + /* Reset saved irqs */ | |
152 | + ams_info.worker_irqs = 0; | |
153 | + | |
154 | + /* Register shock interrupt handler */ | |
155 | + result = pmf_register_irq_client(ams_info.of_node, | |
156 | + "accel-int-2", | |
157 | + &ams_shock_client); | |
158 | + if (result < 0) | |
159 | + goto release_freefall; | |
160 | + | |
161 | + /* Create device */ | |
162 | + ams_info.of_dev = of_platform_device_create(ams_info.of_node, "ams", NULL); | |
163 | + if (!ams_info.of_dev) { | |
164 | + result = -ENODEV; | |
165 | + goto release_shock; | |
166 | + } | |
167 | + | |
168 | + /* Create attributes */ | |
169 | + result = device_create_file(&ams_info.of_dev->dev, &dev_attr_current); | |
170 | + if (result) | |
171 | + goto release_of; | |
172 | + | |
173 | + ams_info.vflag = !!(ams_info.get_vendor() & 0x10); | |
174 | + | |
175 | + /* Init input device */ | |
176 | + result = ams_input_init(); | |
177 | + if (result) | |
178 | + goto release_device_file; | |
179 | + | |
180 | + return result; | |
181 | +release_device_file: | |
182 | + device_remove_file(&ams_info.of_dev->dev, &dev_attr_current); | |
183 | +release_of: | |
184 | + of_device_unregister(ams_info.of_dev); | |
185 | +release_shock: | |
186 | + pmf_unregister_irq_client(&ams_shock_client); | |
187 | +release_freefall: | |
188 | + pmf_unregister_irq_client(&ams_freefall_client); | |
189 | + return result; | |
190 | +} | |
191 | + | |
192 | +int __init ams_init(void) | |
193 | +{ | |
194 | + struct device_node *np; | |
195 | + | |
196 | + spin_lock_init(&ams_info.irq_lock); | |
197 | + mutex_init(&ams_info.lock); | |
198 | + INIT_WORK(&ams_info.worker, ams_worker); | |
199 | + | |
200 | +#ifdef CONFIG_SENSORS_AMS_I2C | |
201 | + np = of_find_node_by_name(NULL, "accelerometer"); | |
202 | + if (np && of_device_is_compatible(np, "AAPL,accelerometer_1")) | |
203 | + /* Found I2C motion sensor */ | |
204 | + return ams_i2c_init(np); | |
205 | +#endif | |
206 | + | |
207 | +#ifdef CONFIG_SENSORS_AMS_PMU | |
208 | + np = of_find_node_by_name(NULL, "sms"); | |
209 | + if (np && of_device_is_compatible(np, "sms")) | |
210 | + /* Found PMU motion sensor */ | |
211 | + return ams_pmu_init(np); | |
212 | +#endif | |
213 | + return -ENODEV; | |
214 | +} | |
215 | + | |
216 | +void ams_sensor_detach(void) | |
217 | +{ | |
218 | + /* Remove input device */ | |
219 | + ams_input_exit(); | |
220 | + | |
221 | + /* Remove attributes */ | |
222 | + device_remove_file(&ams_info.of_dev->dev, &dev_attr_current); | |
223 | + | |
224 | + /* Flush interrupt worker | |
225 | + * | |
226 | + * We do this after ams_info.exit(), because an interrupt might | |
227 | + * have arrived before disabling them. | |
228 | + */ | |
229 | + flush_scheduled_work(); | |
230 | + | |
231 | + /* Remove device */ | |
232 | + of_device_unregister(ams_info.of_dev); | |
233 | + | |
234 | + /* Remove handler */ | |
235 | + pmf_unregister_irq_client(&ams_shock_client); | |
236 | + pmf_unregister_irq_client(&ams_freefall_client); | |
237 | +} | |
238 | + | |
239 | +static void __exit ams_exit(void) | |
240 | +{ | |
241 | + /* Shut down implementation */ | |
242 | + ams_info.exit(); | |
243 | +} | |
244 | + | |
245 | +MODULE_AUTHOR("Stelian Pop, Michael Hanselmann"); | |
246 | +MODULE_DESCRIPTION("Apple Motion Sensor driver"); | |
247 | +MODULE_LICENSE("GPL"); | |
248 | + | |
249 | +module_init(ams_init); | |
250 | +module_exit(ams_exit); |
drivers/macintosh/ams/ams-i2c.c
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 | + ams_sensor_detach(); | |
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 | + | |
262 | +int __init ams_i2c_init(struct device_node *np) | |
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 | +} |
drivers/macintosh/ams/ams-input.c
1 | +/* | |
2 | + * Apple Motion Sensor driver (joystick emulation) | |
3 | + * | |
4 | + * Copyright (C) 2005 Stelian Pop (stelian@popies.net) | |
5 | + * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch) | |
6 | + * | |
7 | + * This program is free software; you can redistribute it and/or modify | |
8 | + * it under the terms of the GNU General Public License as published by | |
9 | + * the Free Software Foundation; either version 2 of the License, or | |
10 | + * (at your option) any later version. | |
11 | + */ | |
12 | + | |
13 | +#include <linux/module.h> | |
14 | + | |
15 | +#include <linux/types.h> | |
16 | +#include <linux/errno.h> | |
17 | +#include <linux/init.h> | |
18 | +#include <linux/delay.h> | |
19 | + | |
20 | +#include "ams.h" | |
21 | + | |
22 | +static unsigned int joystick; | |
23 | +module_param(joystick, bool, S_IRUGO); | |
24 | +MODULE_PARM_DESC(joystick, "Enable the input class device on module load"); | |
25 | + | |
26 | +static unsigned int invert; | |
27 | +module_param(invert, bool, S_IWUSR | S_IRUGO); | |
28 | +MODULE_PARM_DESC(invert, "Invert input data on X and Y axis"); | |
29 | + | |
30 | +static DEFINE_MUTEX(ams_input_mutex); | |
31 | + | |
32 | +static void ams_idev_poll(struct input_polled_dev *dev) | |
33 | +{ | |
34 | + struct input_dev *idev = dev->input; | |
35 | + s8 x, y, z; | |
36 | + | |
37 | + mutex_lock(&ams_info.lock); | |
38 | + | |
39 | + ams_sensors(&x, &y, &z); | |
40 | + | |
41 | + x -= ams_info.xcalib; | |
42 | + y -= ams_info.ycalib; | |
43 | + z -= ams_info.zcalib; | |
44 | + | |
45 | + input_report_abs(idev, ABS_X, invert ? -x : x); | |
46 | + input_report_abs(idev, ABS_Y, invert ? -y : y); | |
47 | + input_report_abs(idev, ABS_Z, z); | |
48 | + | |
49 | + input_sync(idev); | |
50 | + | |
51 | + mutex_unlock(&ams_info.lock); | |
52 | +} | |
53 | + | |
54 | +/* Call with ams_info.lock held! */ | |
55 | +static int ams_input_enable(void) | |
56 | +{ | |
57 | + struct input_dev *input; | |
58 | + s8 x, y, z; | |
59 | + int error; | |
60 | + | |
61 | + ams_sensors(&x, &y, &z); | |
62 | + ams_info.xcalib = x; | |
63 | + ams_info.ycalib = y; | |
64 | + ams_info.zcalib = z; | |
65 | + | |
66 | + ams_info.idev = input_allocate_polled_device(); | |
67 | + if (!ams_info.idev) | |
68 | + return -ENOMEM; | |
69 | + | |
70 | + ams_info.idev->poll = ams_idev_poll; | |
71 | + ams_info.idev->poll_interval = 25; | |
72 | + | |
73 | + input = ams_info.idev->input; | |
74 | + input->name = "Apple Motion Sensor"; | |
75 | + input->id.bustype = ams_info.bustype; | |
76 | + input->id.vendor = 0; | |
77 | + input->dev.parent = &ams_info.of_dev->dev; | |
78 | + | |
79 | + input_set_abs_params(input, ABS_X, -50, 50, 3, 0); | |
80 | + input_set_abs_params(input, ABS_Y, -50, 50, 3, 0); | |
81 | + input_set_abs_params(input, ABS_Z, -50, 50, 3, 0); | |
82 | + | |
83 | + set_bit(EV_ABS, input->evbit); | |
84 | + set_bit(EV_KEY, input->evbit); | |
85 | + set_bit(BTN_TOUCH, input->keybit); | |
86 | + | |
87 | + error = input_register_polled_device(ams_info.idev); | |
88 | + if (error) { | |
89 | + input_free_polled_device(ams_info.idev); | |
90 | + ams_info.idev = NULL; | |
91 | + return error; | |
92 | + } | |
93 | + | |
94 | + joystick = 1; | |
95 | + | |
96 | + return 0; | |
97 | +} | |
98 | + | |
99 | +static void ams_input_disable(void) | |
100 | +{ | |
101 | + if (ams_info.idev) { | |
102 | + input_unregister_polled_device(ams_info.idev); | |
103 | + input_free_polled_device(ams_info.idev); | |
104 | + ams_info.idev = NULL; | |
105 | + } | |
106 | + | |
107 | + joystick = 0; | |
108 | +} | |
109 | + | |
110 | +static ssize_t ams_input_show_joystick(struct device *dev, | |
111 | + struct device_attribute *attr, char *buf) | |
112 | +{ | |
113 | + return sprintf(buf, "%d\n", joystick); | |
114 | +} | |
115 | + | |
116 | +static ssize_t ams_input_store_joystick(struct device *dev, | |
117 | + struct device_attribute *attr, const char *buf, size_t count) | |
118 | +{ | |
119 | + unsigned long enable; | |
120 | + int error = 0; | |
121 | + | |
122 | + if (strict_strtoul(buf, 0, &enable) || enable > 1) | |
123 | + return -EINVAL; | |
124 | + | |
125 | + mutex_lock(&ams_input_mutex); | |
126 | + | |
127 | + if (enable != joystick) { | |
128 | + if (enable) | |
129 | + error = ams_input_enable(); | |
130 | + else | |
131 | + ams_input_disable(); | |
132 | + } | |
133 | + | |
134 | + mutex_unlock(&ams_input_mutex); | |
135 | + | |
136 | + return error ? error : count; | |
137 | +} | |
138 | + | |
139 | +static DEVICE_ATTR(joystick, S_IRUGO | S_IWUSR, | |
140 | + ams_input_show_joystick, ams_input_store_joystick); | |
141 | + | |
142 | +int ams_input_init(void) | |
143 | +{ | |
144 | + if (joystick) | |
145 | + ams_input_enable(); | |
146 | + | |
147 | + return device_create_file(&ams_info.of_dev->dev, &dev_attr_joystick); | |
148 | +} | |
149 | + | |
150 | +void ams_input_exit(void) | |
151 | +{ | |
152 | + device_remove_file(&ams_info.of_dev->dev, &dev_attr_joystick); | |
153 | + | |
154 | + mutex_lock(&ams_input_mutex); | |
155 | + ams_input_disable(); | |
156 | + mutex_unlock(&ams_input_mutex); | |
157 | +} |
drivers/macintosh/ams/ams-pmu.c
1 | +/* | |
2 | + * Apple Motion Sensor driver (PMU variant) | |
3 | + * | |
4 | + * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch) | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or modify | |
7 | + * it under the terms of the GNU General Public License as published by | |
8 | + * the Free Software Foundation; either version 2 of the License, or | |
9 | + * (at your option) any later version. | |
10 | + */ | |
11 | + | |
12 | +#include <linux/module.h> | |
13 | +#include <linux/types.h> | |
14 | +#include <linux/errno.h> | |
15 | +#include <linux/init.h> | |
16 | +#include <linux/adb.h> | |
17 | +#include <linux/pmu.h> | |
18 | + | |
19 | +#include "ams.h" | |
20 | + | |
21 | +/* Attitude */ | |
22 | +#define AMS_X 0x00 | |
23 | +#define AMS_Y 0x01 | |
24 | +#define AMS_Z 0x02 | |
25 | + | |
26 | +/* Not exactly known, maybe chip vendor */ | |
27 | +#define AMS_VENDOR 0x03 | |
28 | + | |
29 | +/* Freefall registers */ | |
30 | +#define AMS_FF_CLEAR 0x04 | |
31 | +#define AMS_FF_ENABLE 0x05 | |
32 | +#define AMS_FF_LOW_LIMIT 0x06 | |
33 | +#define AMS_FF_DEBOUNCE 0x07 | |
34 | + | |
35 | +/* Shock registers */ | |
36 | +#define AMS_SHOCK_CLEAR 0x08 | |
37 | +#define AMS_SHOCK_ENABLE 0x09 | |
38 | +#define AMS_SHOCK_HIGH_LIMIT 0x0a | |
39 | +#define AMS_SHOCK_DEBOUNCE 0x0b | |
40 | + | |
41 | +/* Global interrupt and power control register */ | |
42 | +#define AMS_CONTROL 0x0c | |
43 | + | |
44 | +static u8 ams_pmu_cmd; | |
45 | + | |
46 | +static void ams_pmu_req_complete(struct adb_request *req) | |
47 | +{ | |
48 | + complete((struct completion *)req->arg); | |
49 | +} | |
50 | + | |
51 | +/* Only call this function from task context */ | |
52 | +static void ams_pmu_set_register(u8 reg, u8 value) | |
53 | +{ | |
54 | + static struct adb_request req; | |
55 | + DECLARE_COMPLETION(req_complete); | |
56 | + | |
57 | + req.arg = &req_complete; | |
58 | + if (pmu_request(&req, ams_pmu_req_complete, 4, ams_pmu_cmd, 0x00, reg, value)) | |
59 | + return; | |
60 | + | |
61 | + wait_for_completion(&req_complete); | |
62 | +} | |
63 | + | |
64 | +/* Only call this function from task context */ | |
65 | +static u8 ams_pmu_get_register(u8 reg) | |
66 | +{ | |
67 | + static struct adb_request req; | |
68 | + DECLARE_COMPLETION(req_complete); | |
69 | + | |
70 | + req.arg = &req_complete; | |
71 | + if (pmu_request(&req, ams_pmu_req_complete, 3, ams_pmu_cmd, 0x01, reg)) | |
72 | + return 0; | |
73 | + | |
74 | + wait_for_completion(&req_complete); | |
75 | + | |
76 | + if (req.reply_len > 0) | |
77 | + return req.reply[0]; | |
78 | + else | |
79 | + return 0; | |
80 | +} | |
81 | + | |
82 | +/* Enables or disables the specified interrupts */ | |
83 | +static void ams_pmu_set_irq(enum ams_irq reg, char enable) | |
84 | +{ | |
85 | + if (reg & AMS_IRQ_FREEFALL) { | |
86 | + u8 val = ams_pmu_get_register(AMS_FF_ENABLE); | |
87 | + if (enable) | |
88 | + val |= 0x80; | |
89 | + else | |
90 | + val &= ~0x80; | |
91 | + ams_pmu_set_register(AMS_FF_ENABLE, val); | |
92 | + } | |
93 | + | |
94 | + if (reg & AMS_IRQ_SHOCK) { | |
95 | + u8 val = ams_pmu_get_register(AMS_SHOCK_ENABLE); | |
96 | + if (enable) | |
97 | + val |= 0x80; | |
98 | + else | |
99 | + val &= ~0x80; | |
100 | + ams_pmu_set_register(AMS_SHOCK_ENABLE, val); | |
101 | + } | |
102 | + | |
103 | + if (reg & AMS_IRQ_GLOBAL) { | |
104 | + u8 val = ams_pmu_get_register(AMS_CONTROL); | |
105 | + if (enable) | |
106 | + val |= 0x80; | |
107 | + else | |
108 | + val &= ~0x80; | |
109 | + ams_pmu_set_register(AMS_CONTROL, val); | |
110 | + } | |
111 | +} | |
112 | + | |
113 | +static void ams_pmu_clear_irq(enum ams_irq reg) | |
114 | +{ | |
115 | + if (reg & AMS_IRQ_FREEFALL) | |
116 | + ams_pmu_set_register(AMS_FF_CLEAR, 0x00); | |
117 | + | |
118 | + if (reg & AMS_IRQ_SHOCK) | |
119 | + ams_pmu_set_register(AMS_SHOCK_CLEAR, 0x00); | |
120 | +} | |
121 | + | |
122 | +static u8 ams_pmu_get_vendor(void) | |
123 | +{ | |
124 | + return ams_pmu_get_register(AMS_VENDOR); | |
125 | +} | |
126 | + | |
127 | +static void ams_pmu_get_xyz(s8 *x, s8 *y, s8 *z) | |
128 | +{ | |
129 | + *x = ams_pmu_get_register(AMS_X); | |
130 | + *y = ams_pmu_get_register(AMS_Y); | |
131 | + *z = ams_pmu_get_register(AMS_Z); | |
132 | +} | |
133 | + | |
134 | +static void ams_pmu_exit(void) | |
135 | +{ | |
136 | + ams_sensor_detach(); | |
137 | + | |
138 | + /* Disable interrupts */ | |
139 | + ams_pmu_set_irq(AMS_IRQ_ALL, 0); | |
140 | + | |
141 | + /* Clear interrupts */ | |
142 | + ams_pmu_clear_irq(AMS_IRQ_ALL); | |
143 | + | |
144 | + ams_info.has_device = 0; | |
145 | + | |
146 | + printk(KERN_INFO "ams: Unloading\n"); | |
147 | +} | |
148 | + | |
149 | +int __init ams_pmu_init(struct device_node *np) | |
150 | +{ | |
151 | + const u32 *prop; | |
152 | + int result; | |
153 | + | |
154 | + /* Set implementation stuff */ | |
155 | + ams_info.of_node = np; | |
156 | + ams_info.exit = ams_pmu_exit; | |
157 | + ams_info.get_vendor = ams_pmu_get_vendor; | |
158 | + ams_info.get_xyz = ams_pmu_get_xyz; | |
159 | + ams_info.clear_irq = ams_pmu_clear_irq; | |
160 | + ams_info.bustype = BUS_HOST; | |
161 | + | |
162 | + /* Get PMU command, should be 0x4e, but we can never know */ | |
163 | + prop = of_get_property(ams_info.of_node, "reg", NULL); | |
164 | + if (!prop) | |
165 | + return -ENODEV; | |
166 | + | |
167 | + ams_pmu_cmd = ((*prop) >> 8) & 0xff; | |
168 | + | |
169 | + /* Disable interrupts */ | |
170 | + ams_pmu_set_irq(AMS_IRQ_ALL, 0); | |
171 | + | |
172 | + /* Clear interrupts */ | |
173 | + ams_pmu_clear_irq(AMS_IRQ_ALL); | |
174 | + | |
175 | + result = ams_sensor_attach(); | |
176 | + if (result < 0) | |
177 | + return result; | |
178 | + | |
179 | + /* Set default values */ | |
180 | + ams_pmu_set_register(AMS_FF_LOW_LIMIT, 0x15); | |
181 | + ams_pmu_set_register(AMS_FF_ENABLE, 0x08); | |
182 | + ams_pmu_set_register(AMS_FF_DEBOUNCE, 0x14); | |
183 | + | |
184 | + ams_pmu_set_register(AMS_SHOCK_HIGH_LIMIT, 0x60); | |
185 | + ams_pmu_set_register(AMS_SHOCK_ENABLE, 0x0f); | |
186 | + ams_pmu_set_register(AMS_SHOCK_DEBOUNCE, 0x14); | |
187 | + | |
188 | + ams_pmu_set_register(AMS_CONTROL, 0x4f); | |
189 | + | |
190 | + /* Clear interrupts */ | |
191 | + ams_pmu_clear_irq(AMS_IRQ_ALL); | |
192 | + | |
193 | + ams_info.has_device = 1; | |
194 | + | |
195 | + /* Enable interrupts */ | |
196 | + ams_pmu_set_irq(AMS_IRQ_ALL, 1); | |
197 | + | |
198 | + printk(KERN_INFO "ams: Found PMU based motion sensor\n"); | |
199 | + | |
200 | + return 0; | |
201 | +} |
drivers/macintosh/ams/ams.h
1 | +#include <linux/i2c.h> | |
2 | +#include <linux/input-polldev.h> | |
3 | +#include <linux/kthread.h> | |
4 | +#include <linux/mutex.h> | |
5 | +#include <linux/spinlock.h> | |
6 | +#include <linux/types.h> | |
7 | +#include <linux/of_device.h> | |
8 | + | |
9 | +enum ams_irq { | |
10 | + AMS_IRQ_FREEFALL = 0x01, | |
11 | + AMS_IRQ_SHOCK = 0x02, | |
12 | + AMS_IRQ_GLOBAL = 0x04, | |
13 | + AMS_IRQ_ALL = | |
14 | + AMS_IRQ_FREEFALL | | |
15 | + AMS_IRQ_SHOCK | | |
16 | + AMS_IRQ_GLOBAL, | |
17 | +}; | |
18 | + | |
19 | +struct ams { | |
20 | + /* Locks */ | |
21 | + spinlock_t irq_lock; | |
22 | + struct mutex lock; | |
23 | + | |
24 | + /* General properties */ | |
25 | + struct device_node *of_node; | |
26 | + struct platform_device *of_dev; | |
27 | + char has_device; | |
28 | + char vflag; | |
29 | + u32 orient1; | |
30 | + u32 orient2; | |
31 | + | |
32 | + /* Interrupt worker */ | |
33 | + struct work_struct worker; | |
34 | + u8 worker_irqs; | |
35 | + | |
36 | + /* Implementation | |
37 | + * | |
38 | + * Only call these functions with the main lock held. | |
39 | + */ | |
40 | + void (*exit)(void); | |
41 | + | |
42 | + void (*get_xyz)(s8 *x, s8 *y, s8 *z); | |
43 | + u8 (*get_vendor)(void); | |
44 | + | |
45 | + void (*clear_irq)(enum ams_irq reg); | |
46 | + | |
47 | +#ifdef CONFIG_SENSORS_AMS_I2C | |
48 | + /* I2C properties */ | |
49 | + struct i2c_client *i2c_client; | |
50 | +#endif | |
51 | + | |
52 | + /* Joystick emulation */ | |
53 | + struct input_polled_dev *idev; | |
54 | + __u16 bustype; | |
55 | + | |
56 | + /* calibrated null values */ | |
57 | + int xcalib, ycalib, zcalib; | |
58 | +}; | |
59 | + | |
60 | +extern struct ams ams_info; | |
61 | + | |
62 | +extern void ams_sensors(s8 *x, s8 *y, s8 *z); | |
63 | +extern int ams_sensor_attach(void); | |
64 | +extern void ams_sensor_detach(void); | |
65 | + | |
66 | +extern int ams_pmu_init(struct device_node *np); | |
67 | +extern int ams_i2c_init(struct device_node *np); | |
68 | + | |
69 | +extern int ams_input_init(void); | |
70 | +extern void ams_input_exit(void); |