aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hwmon/ams/ams-core.c
diff options
context:
space:
mode:
authorStelian Pop <stelian@popies.net>2006-12-12 12:18:30 -0500
committerJean Delvare <khali@arrakis.delvare>2006-12-12 12:18:30 -0500
commitdcb69dd010c182507a8db8940618f6413f65e0af (patch)
treeb0a2d0795866d7d787af82be6041cf3cabb6c640 /drivers/hwmon/ams/ams-core.c
parent61db011d403388b2dd342489d7110cf13cb99025 (diff)
hwmon: New AMS hardware monitoring driver
This driver adds support for the Apple Motion Sensor (AMS) as found in 2005 revisions of Apple PowerBooks and iBooks. It implements both the PMU and I2C variants. The I2C driver and mouse emulation is based on code by Stelian Pop, while the PMU driver has been developped by Michael Hanselmann. HD parking support will be added later. Various people contributed fixes to this driver, including Aristeu Sergio Rozanski Filho and Jean Delvare. Signed-off-by: Stelian Pop <stelian@popies.net> Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> Acked-by: Robert Love <rml@novell.com> Signed-off-by: Jean Delvare <khali@linux-fr.org>
Diffstat (limited to 'drivers/hwmon/ams/ams-core.c')
-rw-r--r--drivers/hwmon/ams/ams-core.c265
1 files changed, 265 insertions, 0 deletions
diff --git a/drivers/hwmon/ams/ams-core.c b/drivers/hwmon/ams/ams-core.c
new file mode 100644
index 000000000000..f1f0f5d0442c
--- /dev/null
+++ b/drivers/hwmon/ams/ams-core.c
@@ -0,0 +1,265 @@
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/module.h>
27#include <asm/pmac_pfunc.h>
28#include <asm/of_platform.h>
29
30#include "ams.h"
31
32/* There is only one motion sensor per machine */
33struct ams ams_info;
34
35static unsigned int verbose;
36module_param(verbose, bool, 0644);
37MODULE_PARM_DESC(verbose, "Show free falls and shocks in kernel output");
38
39/* Call with ams_info.lock held! */
40void ams_sensors(s8 *x, s8 *y, s8 *z)
41{
42 u32 orient = ams_info.vflag? ams_info.orient1 : ams_info.orient2;
43
44 if (orient & 0x80)
45 /* X and Y swapped */
46 ams_info.get_xyz(y, x, z);
47 else
48 ams_info.get_xyz(x, y, z);
49
50 if (orient & 0x04)
51 *z = ~(*z);
52 if (orient & 0x02)
53 *y = ~(*y);
54 if (orient & 0x01)
55 *x = ~(*x);
56}
57
58static ssize_t ams_show_current(struct device *dev,
59 struct device_attribute *attr, char *buf)
60{
61 s8 x, y, z;
62
63 mutex_lock(&ams_info.lock);
64 ams_sensors(&x, &y, &z);
65 mutex_unlock(&ams_info.lock);
66
67 return snprintf(buf, PAGE_SIZE, "%d %d %d\n", x, y, z);
68}
69
70static DEVICE_ATTR(current, S_IRUGO, ams_show_current, NULL);
71
72static void ams_handle_irq(void *data)
73{
74 enum ams_irq irq = *((enum ams_irq *)data);
75
76 spin_lock(&ams_info.irq_lock);
77
78 ams_info.worker_irqs |= irq;
79 schedule_work(&ams_info.worker);
80
81 spin_unlock(&ams_info.irq_lock);
82}
83
84static enum ams_irq ams_freefall_irq_data = AMS_IRQ_FREEFALL;
85static struct pmf_irq_client ams_freefall_client = {
86 .owner = THIS_MODULE,
87 .handler = ams_handle_irq,
88 .data = &ams_freefall_irq_data,
89};
90
91static enum ams_irq ams_shock_irq_data = AMS_IRQ_SHOCK;
92static struct pmf_irq_client ams_shock_client = {
93 .owner = THIS_MODULE,
94 .handler = ams_handle_irq,
95 .data = &ams_shock_irq_data,
96};
97
98/* Once hard disk parking is implemented in the kernel, this function can
99 * trigger it.
100 */
101static void ams_worker(struct work_struct *work)
102{
103 mutex_lock(&ams_info.lock);
104
105 if (ams_info.has_device) {
106 unsigned long flags;
107
108 spin_lock_irqsave(&ams_info.irq_lock, flags);
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 /* we must call this with interrupts enabled */
117 spin_unlock_irqrestore(&ams_info.irq_lock, flags);
118 ams_info.clear_irq(AMS_IRQ_FREEFALL);
119 spin_lock_irqsave(&ams_info.irq_lock, flags);
120 }
121
122 if (ams_info.worker_irqs & AMS_IRQ_SHOCK) {
123 if (verbose)
124 printk(KERN_INFO "ams: shock detected!\n");
125
126 ams_info.worker_irqs &= ~AMS_IRQ_SHOCK;
127
128 /* we must call this with interrupts enabled */
129 spin_unlock_irqrestore(&ams_info.irq_lock, flags);
130 ams_info.clear_irq(AMS_IRQ_SHOCK);
131 spin_lock_irqsave(&ams_info.irq_lock, flags);
132 }
133
134 spin_unlock_irqrestore(&ams_info.irq_lock, flags);
135 }
136
137 mutex_unlock(&ams_info.lock);
138}
139
140/* Call with ams_info.lock held! */
141int ams_sensor_attach(void)
142{
143 int result;
144 u32 *prop;
145
146 /* Get orientation */
147 prop = (u32*)get_property(ams_info.of_node, "orientation", NULL);
148 if (!prop)
149 return -ENODEV;
150 ams_info.orient1 = *prop;
151 ams_info.orient2 = *(prop + 1);
152
153 /* Register freefall interrupt handler */
154 result = pmf_register_irq_client(ams_info.of_node,
155 "accel-int-1",
156 &ams_freefall_client);
157 if (result < 0)
158 return -ENODEV;
159
160 /* Reset saved irqs */
161 ams_info.worker_irqs = 0;
162
163 /* Register shock interrupt handler */
164 result = pmf_register_irq_client(ams_info.of_node,
165 "accel-int-2",
166 &ams_shock_client);
167 if (result < 0)
168 goto release_freefall;
169
170 /* Create device */
171 ams_info.of_dev = of_platform_device_create(ams_info.of_node, "ams", NULL);
172 if (!ams_info.of_dev) {
173 result = -ENODEV;
174 goto release_shock;
175 }
176
177 /* Create attributes */
178 result = device_create_file(&ams_info.of_dev->dev, &dev_attr_current);
179 if (result)
180 goto release_of;
181
182 ams_info.vflag = !!(ams_info.get_vendor() & 0x10);
183
184 /* Init input device */
185 result = ams_input_init();
186 if (result)
187 goto release_device_file;
188
189 return result;
190release_device_file:
191 device_remove_file(&ams_info.of_dev->dev, &dev_attr_current);
192release_of:
193 of_device_unregister(ams_info.of_dev);
194release_shock:
195 pmf_unregister_irq_client(&ams_shock_client);
196release_freefall:
197 pmf_unregister_irq_client(&ams_freefall_client);
198 return result;
199}
200
201int __init ams_init(void)
202{
203 struct device_node *np;
204
205 spin_lock_init(&ams_info.irq_lock);
206 mutex_init(&ams_info.lock);
207 INIT_WORK(&ams_info.worker, ams_worker);
208
209#ifdef CONFIG_SENSORS_AMS_I2C
210 np = of_find_node_by_name(NULL, "accelerometer");
211 if (np && device_is_compatible(np, "AAPL,accelerometer_1"))
212 /* Found I2C motion sensor */
213 return ams_i2c_init(np);
214#endif
215
216#ifdef CONFIG_SENSORS_AMS_PMU
217 np = of_find_node_by_name(NULL, "sms");
218 if (np && device_is_compatible(np, "sms"))
219 /* Found PMU motion sensor */
220 return ams_pmu_init(np);
221#endif
222
223 printk(KERN_ERR "ams: No motion sensor found.\n");
224
225 return -ENODEV;
226}
227
228void ams_exit(void)
229{
230 mutex_lock(&ams_info.lock);
231
232 if (ams_info.has_device) {
233 /* Remove input device */
234 ams_input_exit();
235
236 /* Shut down implementation */
237 ams_info.exit();
238
239 /* Flush interrupt worker
240 *
241 * We do this after ams_info.exit(), because an interrupt might
242 * have arrived before disabling them.
243 */
244 flush_scheduled_work();
245
246 /* Remove attributes */
247 device_remove_file(&ams_info.of_dev->dev, &dev_attr_current);
248
249 /* Remove device */
250 of_device_unregister(ams_info.of_dev);
251
252 /* Remove handler */
253 pmf_unregister_irq_client(&ams_shock_client);
254 pmf_unregister_irq_client(&ams_freefall_client);
255 }
256
257 mutex_unlock(&ams_info.lock);
258}
259
260MODULE_AUTHOR("Stelian Pop, Michael Hanselmann");
261MODULE_DESCRIPTION("Apple Motion Sensor driver");
262MODULE_LICENSE("GPL");
263
264module_init(ams_init);
265module_exit(ams_exit);