blob: a51c9ae9904286048eaea356cdc6ec27cecb698a [file] [log] [blame]
Matthew Qin25e55182014-02-11 16:28:50 +08001/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are
5 * met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above
9 * copyright notice, this list of conditions and the following
10 * disclaimer in the documentation and/or other materials provided
11 * with the distribution.
12 * * Neither the name of The Linux Foundation, Inc. nor the names of its
13 * contributors may be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <debug.h>
30#include <reg.h>
31#include <stdlib.h>
32#include <pm8x41.h>
33#include <pm8x41_hw.h>
34#include <kernel/timer.h>
35#include <platform/timer.h>
36#include <shutdown_detect.h>
37
38/* sleep clock is 32.768 khz, 0x8000 count per second */
39#define MPM_SLEEP_TIMETICK_COUNT 0x8000
40#define PWRKEY_LONG_PRESS_COUNT 0xC000
41#define QPNP_DEFAULT_TIMEOUT 250
42#define PWRKEY_DETECT_FREQUENCY 50
43
44static struct timer pon_timer;
45static uint32_t pon_timer_complete = 0;
46
47/*
48 * Function to check if the the power key is pressed long enough.
49 * Return 0 if boot time more than PWRKEY_LONG_PRESS_COUNT.
50 * Return 1 if boot time less than PWRKEY_LONG_PRESS_COUNT.
51 */
52static uint32_t is_pwrkey_time_expired()
53{
54 /* power on button tied in with PMIC KPDPWR. */
55 uint32_t sclk_count = platform_get_sclk_count();
56
57 /* Here check if the long press power-key lasts for 1.5s */
58 if (sclk_count > PWRKEY_LONG_PRESS_COUNT)
59 return 0;
60 else
61 return 1;
62}
63
64/*
65 * Function to check if the power on reason is power key triggered.
66 * Return 1 if it is triggered by power key.
67 * Return 0 if it is not triggered by power key.
68 */
69static uint32_t is_pwrkey_pon_reason()
70{
71 uint8_t pon_reason = pm8x41_get_pon_reason();
72
73 if (pm8x41_get_is_cold_boot() && (pon_reason == KPDPWR_N))
74 return 1;
75 else
76 return 0;
77}
78
79/*
80 * Main timer handle: check every PWRKEY_DETECT_FREQUENCY to see
81 * if power key is pressed.
82 * Shutdown the device if power key is release before
83 * (PWRKEY_LONG_PRESS_COUNT/MPM_SLEEP_TIMETICK_COUNT) seconds.
84 */
85static enum handler_return long_press_pwrkey_timer_func(struct timer *p_timer,
86 void *arg)
87{
88 uint32_t sclk_count = platform_get_sclk_count();
89
90 /*
91 * The following condition is treated as the power key
92 * is pressed long enough.
93 * 1. if the power key is pressed last for PWRKEY_LONG_PRESS_COUNT.
94 */
95 if (sclk_count > PWRKEY_LONG_PRESS_COUNT) {
96 timer_cancel(p_timer);
97 pon_timer_complete = 1;
98 } else {
99 if (pm8x41_get_pwrkey_is_pressed()) {
100 /*
101 * For normal man response is > 0.1 secs, so we use 0.05 secs default
102 * for software to be safely detect if there is a key release action.
103 */
104 timer_set_oneshot(p_timer, PWRKEY_DETECT_FREQUENCY,
105 long_press_pwrkey_timer_func, NULL);
106 } else {
107 shutdown_device();
108 }
109 }
110
111 return INT_RESCHEDULE;
112}
113
114/*
115 * Function to wait until the power key is pressed long enough
116 */
117static void wait_for_long_pwrkey_pressed()
118{
119 uint32_t sclk_count = 0;
120
121 while (!pon_timer_complete) {
122 sclk_count = platform_get_sclk_count();
123 if (sclk_count > PWRKEY_LONG_PRESS_COUNT) {
124 timer_cancel(&pon_timer);
125 break;
126 }
127 }
128}
129
130/*
131 * Function to support for shutdown detection
132 * If below condition is met, the function will shut down
133 * the device. Otherwise it will do nothing and return to
134 * normal boot.
135 * condition:
136 * 1. it is triggered by power key &&
137 * 2. the power key is released before
138 * (PWRKEY_LONG_PRESS_COUNT/MPM_SLEEP_TIMETICK_COUNT) seconds.
139 */
140void shutdown_detect()
141{
142 /*
143 * If it is booted by power key tirigger.
144 * Initialize pon_timer and call long_press_pwrkey_timer_func
145 * function to check if the power key is last press long enough.
146 */
147 if (is_pwrkey_pon_reason() && is_pwrkey_time_expired()) {
148 timer_initialize(&pon_timer);
149 timer_set_oneshot(&pon_timer, 0, long_press_pwrkey_timer_func, NULL);
150
151 /*
152 * Wait until long press power key timeout
153 *
154 * It will be confused to end users if we shutdown the device
155 * after the splash screen displayed. But it can be moved the
156 * wait here if the boot time is much more considered.
157 */
158 wait_for_long_pwrkey_pressed();
159 }
160}