Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/macintosh/ams/ams-pmu.c
15115 views
1
/*
2
* Apple Motion Sensor driver (PMU variant)
3
*
4
* Copyright (C) 2006 Michael Hanselmann ([email protected])
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
}
202
203