1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Apple Motion Sensor driver (PMU variant)
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch)
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include <linux/module.h>
9*4882a593Smuzhiyun #include <linux/types.h>
10*4882a593Smuzhiyun #include <linux/errno.h>
11*4882a593Smuzhiyun #include <linux/init.h>
12*4882a593Smuzhiyun #include <linux/adb.h>
13*4882a593Smuzhiyun #include <linux/pmu.h>
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #include "ams.h"
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun /* Attitude */
18*4882a593Smuzhiyun #define AMS_X 0x00
19*4882a593Smuzhiyun #define AMS_Y 0x01
20*4882a593Smuzhiyun #define AMS_Z 0x02
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun /* Not exactly known, maybe chip vendor */
23*4882a593Smuzhiyun #define AMS_VENDOR 0x03
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun /* Freefall registers */
26*4882a593Smuzhiyun #define AMS_FF_CLEAR 0x04
27*4882a593Smuzhiyun #define AMS_FF_ENABLE 0x05
28*4882a593Smuzhiyun #define AMS_FF_LOW_LIMIT 0x06
29*4882a593Smuzhiyun #define AMS_FF_DEBOUNCE 0x07
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun /* Shock registers */
32*4882a593Smuzhiyun #define AMS_SHOCK_CLEAR 0x08
33*4882a593Smuzhiyun #define AMS_SHOCK_ENABLE 0x09
34*4882a593Smuzhiyun #define AMS_SHOCK_HIGH_LIMIT 0x0a
35*4882a593Smuzhiyun #define AMS_SHOCK_DEBOUNCE 0x0b
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun /* Global interrupt and power control register */
38*4882a593Smuzhiyun #define AMS_CONTROL 0x0c
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun static u8 ams_pmu_cmd;
41*4882a593Smuzhiyun
ams_pmu_req_complete(struct adb_request * req)42*4882a593Smuzhiyun static void ams_pmu_req_complete(struct adb_request *req)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun complete((struct completion *)req->arg);
45*4882a593Smuzhiyun }
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun /* Only call this function from task context */
ams_pmu_set_register(u8 reg,u8 value)48*4882a593Smuzhiyun static void ams_pmu_set_register(u8 reg, u8 value)
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun static struct adb_request req;
51*4882a593Smuzhiyun DECLARE_COMPLETION(req_complete);
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun req.arg = &req_complete;
54*4882a593Smuzhiyun if (pmu_request(&req, ams_pmu_req_complete, 4, ams_pmu_cmd, 0x00, reg, value))
55*4882a593Smuzhiyun return;
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun wait_for_completion(&req_complete);
58*4882a593Smuzhiyun }
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun /* Only call this function from task context */
ams_pmu_get_register(u8 reg)61*4882a593Smuzhiyun static u8 ams_pmu_get_register(u8 reg)
62*4882a593Smuzhiyun {
63*4882a593Smuzhiyun static struct adb_request req;
64*4882a593Smuzhiyun DECLARE_COMPLETION(req_complete);
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun req.arg = &req_complete;
67*4882a593Smuzhiyun if (pmu_request(&req, ams_pmu_req_complete, 3, ams_pmu_cmd, 0x01, reg))
68*4882a593Smuzhiyun return 0;
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun wait_for_completion(&req_complete);
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun if (req.reply_len > 0)
73*4882a593Smuzhiyun return req.reply[0];
74*4882a593Smuzhiyun else
75*4882a593Smuzhiyun return 0;
76*4882a593Smuzhiyun }
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun /* Enables or disables the specified interrupts */
ams_pmu_set_irq(enum ams_irq reg,char enable)79*4882a593Smuzhiyun static void ams_pmu_set_irq(enum ams_irq reg, char enable)
80*4882a593Smuzhiyun {
81*4882a593Smuzhiyun if (reg & AMS_IRQ_FREEFALL) {
82*4882a593Smuzhiyun u8 val = ams_pmu_get_register(AMS_FF_ENABLE);
83*4882a593Smuzhiyun if (enable)
84*4882a593Smuzhiyun val |= 0x80;
85*4882a593Smuzhiyun else
86*4882a593Smuzhiyun val &= ~0x80;
87*4882a593Smuzhiyun ams_pmu_set_register(AMS_FF_ENABLE, val);
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun if (reg & AMS_IRQ_SHOCK) {
91*4882a593Smuzhiyun u8 val = ams_pmu_get_register(AMS_SHOCK_ENABLE);
92*4882a593Smuzhiyun if (enable)
93*4882a593Smuzhiyun val |= 0x80;
94*4882a593Smuzhiyun else
95*4882a593Smuzhiyun val &= ~0x80;
96*4882a593Smuzhiyun ams_pmu_set_register(AMS_SHOCK_ENABLE, val);
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun if (reg & AMS_IRQ_GLOBAL) {
100*4882a593Smuzhiyun u8 val = ams_pmu_get_register(AMS_CONTROL);
101*4882a593Smuzhiyun if (enable)
102*4882a593Smuzhiyun val |= 0x80;
103*4882a593Smuzhiyun else
104*4882a593Smuzhiyun val &= ~0x80;
105*4882a593Smuzhiyun ams_pmu_set_register(AMS_CONTROL, val);
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun
ams_pmu_clear_irq(enum ams_irq reg)109*4882a593Smuzhiyun static void ams_pmu_clear_irq(enum ams_irq reg)
110*4882a593Smuzhiyun {
111*4882a593Smuzhiyun if (reg & AMS_IRQ_FREEFALL)
112*4882a593Smuzhiyun ams_pmu_set_register(AMS_FF_CLEAR, 0x00);
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun if (reg & AMS_IRQ_SHOCK)
115*4882a593Smuzhiyun ams_pmu_set_register(AMS_SHOCK_CLEAR, 0x00);
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun
ams_pmu_get_vendor(void)118*4882a593Smuzhiyun static u8 ams_pmu_get_vendor(void)
119*4882a593Smuzhiyun {
120*4882a593Smuzhiyun return ams_pmu_get_register(AMS_VENDOR);
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun
ams_pmu_get_xyz(s8 * x,s8 * y,s8 * z)123*4882a593Smuzhiyun static void ams_pmu_get_xyz(s8 *x, s8 *y, s8 *z)
124*4882a593Smuzhiyun {
125*4882a593Smuzhiyun *x = ams_pmu_get_register(AMS_X);
126*4882a593Smuzhiyun *y = ams_pmu_get_register(AMS_Y);
127*4882a593Smuzhiyun *z = ams_pmu_get_register(AMS_Z);
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun
ams_pmu_exit(void)130*4882a593Smuzhiyun static void ams_pmu_exit(void)
131*4882a593Smuzhiyun {
132*4882a593Smuzhiyun ams_sensor_detach();
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun /* Disable interrupts */
135*4882a593Smuzhiyun ams_pmu_set_irq(AMS_IRQ_ALL, 0);
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun /* Clear interrupts */
138*4882a593Smuzhiyun ams_pmu_clear_irq(AMS_IRQ_ALL);
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun ams_info.has_device = 0;
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun printk(KERN_INFO "ams: Unloading\n");
143*4882a593Smuzhiyun }
144*4882a593Smuzhiyun
ams_pmu_init(struct device_node * np)145*4882a593Smuzhiyun int __init ams_pmu_init(struct device_node *np)
146*4882a593Smuzhiyun {
147*4882a593Smuzhiyun const u32 *prop;
148*4882a593Smuzhiyun int result;
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun /* Set implementation stuff */
151*4882a593Smuzhiyun ams_info.of_node = np;
152*4882a593Smuzhiyun ams_info.exit = ams_pmu_exit;
153*4882a593Smuzhiyun ams_info.get_vendor = ams_pmu_get_vendor;
154*4882a593Smuzhiyun ams_info.get_xyz = ams_pmu_get_xyz;
155*4882a593Smuzhiyun ams_info.clear_irq = ams_pmu_clear_irq;
156*4882a593Smuzhiyun ams_info.bustype = BUS_HOST;
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun /* Get PMU command, should be 0x4e, but we can never know */
159*4882a593Smuzhiyun prop = of_get_property(ams_info.of_node, "reg", NULL);
160*4882a593Smuzhiyun if (!prop)
161*4882a593Smuzhiyun return -ENODEV;
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun ams_pmu_cmd = ((*prop) >> 8) & 0xff;
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun /* Disable interrupts */
166*4882a593Smuzhiyun ams_pmu_set_irq(AMS_IRQ_ALL, 0);
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun /* Clear interrupts */
169*4882a593Smuzhiyun ams_pmu_clear_irq(AMS_IRQ_ALL);
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun result = ams_sensor_attach();
172*4882a593Smuzhiyun if (result < 0)
173*4882a593Smuzhiyun return result;
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun /* Set default values */
176*4882a593Smuzhiyun ams_pmu_set_register(AMS_FF_LOW_LIMIT, 0x15);
177*4882a593Smuzhiyun ams_pmu_set_register(AMS_FF_ENABLE, 0x08);
178*4882a593Smuzhiyun ams_pmu_set_register(AMS_FF_DEBOUNCE, 0x14);
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun ams_pmu_set_register(AMS_SHOCK_HIGH_LIMIT, 0x60);
181*4882a593Smuzhiyun ams_pmu_set_register(AMS_SHOCK_ENABLE, 0x0f);
182*4882a593Smuzhiyun ams_pmu_set_register(AMS_SHOCK_DEBOUNCE, 0x14);
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun ams_pmu_set_register(AMS_CONTROL, 0x4f);
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun /* Clear interrupts */
187*4882a593Smuzhiyun ams_pmu_clear_irq(AMS_IRQ_ALL);
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun ams_info.has_device = 1;
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun /* Enable interrupts */
192*4882a593Smuzhiyun ams_pmu_set_irq(AMS_IRQ_ALL, 1);
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun printk(KERN_INFO "ams: Found PMU based motion sensor\n");
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun return 0;
197*4882a593Smuzhiyun }
198