aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hwmon/ams/ams-pmu.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-pmu.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-pmu.c')
-rw-r--r--drivers/hwmon/ams/ams-pmu.c207
1 files changed, 207 insertions, 0 deletions
diff --git a/drivers/hwmon/ams/ams-pmu.c b/drivers/hwmon/ams/ams-pmu.c
new file mode 100644
index 000000000000..4636ae031a53
--- /dev/null
+++ b/drivers/hwmon/ams/ams-pmu.c
@@ -0,0 +1,207 @@
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
44static u8 ams_pmu_cmd;
45
46static 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 */
52static 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 */
65static 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 */
83static 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
113static 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
122static u8 ams_pmu_get_vendor(void)
123{
124 return ams_pmu_get_register(AMS_VENDOR);
125}
126
127static 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
134static void ams_pmu_exit(void)
135{
136 /* Disable interrupts */
137 ams_pmu_set_irq(AMS_IRQ_ALL, 0);
138
139 /* Clear interrupts */
140 ams_pmu_clear_irq(AMS_IRQ_ALL);
141
142 ams_info.has_device = 0;
143
144 printk(KERN_INFO "ams: Unloading\n");
145}
146
147int __init ams_pmu_init(struct device_node *np)
148{
149 u32 *prop;
150 int result;
151
152 mutex_lock(&ams_info.lock);
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 = (u32*)get_property(ams_info.of_node, "reg", NULL);
164 if (!prop) {
165 result = -ENODEV;
166 goto exit;
167 }
168 ams_pmu_cmd = ((*prop) >> 8) & 0xff;
169
170 /* Disable interrupts */
171 ams_pmu_set_irq(AMS_IRQ_ALL, 0);
172
173 /* Clear interrupts */
174 ams_pmu_clear_irq(AMS_IRQ_ALL);
175
176 result = ams_sensor_attach();
177 if (result < 0)
178 goto exit;
179
180 /* Set default values */
181 ams_pmu_set_register(AMS_FF_LOW_LIMIT, 0x15);
182 ams_pmu_set_register(AMS_FF_ENABLE, 0x08);
183 ams_pmu_set_register(AMS_FF_DEBOUNCE, 0x14);
184
185 ams_pmu_set_register(AMS_SHOCK_HIGH_LIMIT, 0x60);
186 ams_pmu_set_register(AMS_SHOCK_ENABLE, 0x0f);
187 ams_pmu_set_register(AMS_SHOCK_DEBOUNCE, 0x14);
188
189 ams_pmu_set_register(AMS_CONTROL, 0x4f);
190
191 /* Clear interrupts */
192 ams_pmu_clear_irq(AMS_IRQ_ALL);
193
194 ams_info.has_device = 1;
195
196 /* Enable interrupts */
197 ams_pmu_set_irq(AMS_IRQ_ALL, 1);
198
199 printk(KERN_INFO "ams: Found PMU based motion sensor\n");
200
201 result = 0;
202
203exit:
204 mutex_unlock(&ams_info.lock);
205
206 return result;
207}