172 lines
5.3 KiB
C
172 lines
5.3 KiB
C
|
/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms, with or without
|
||
|
* modification, are permitted provided that the following conditions are
|
||
|
* met:
|
||
|
* * Redistributions of source code must retain the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer.
|
||
|
* * Redistributions in binary form must reproduce the above
|
||
|
* copyright notice, this list of conditions and the following
|
||
|
* disclaimer in the documentation and/or other materials provided
|
||
|
* with the distribution.
|
||
|
* * Neither the name of The Linux Foundation, nor the names of its
|
||
|
* contributors may be used to endorse or promote products derived
|
||
|
* from this software without specific prior written permission.
|
||
|
*
|
||
|
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
|
||
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
|
||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||
|
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||
|
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
*/
|
||
|
|
||
|
#include <debug.h>
|
||
|
#include <reg.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <pm8x41.h>
|
||
|
#include <pm8x41_hw.h>
|
||
|
#include <kernel/timer.h>
|
||
|
#include <kernel/thread.h>
|
||
|
#include <platform/timer.h>
|
||
|
#include <shutdown_detect.h>
|
||
|
#include <platform.h>
|
||
|
#include <target.h>
|
||
|
|
||
|
/* sleep clock is 32.768 khz, 0x8000 count per second */
|
||
|
#define MPM_SLEEP_TIMETICK_COUNT 0x8000
|
||
|
#define PWRKEY_LONG_PRESS_COUNT 0xC000
|
||
|
#define QPNP_DEFAULT_TIMEOUT 250
|
||
|
#define PWRKEY_DETECT_FREQUENCY 50
|
||
|
|
||
|
static struct timer pon_timer;
|
||
|
static uint32_t pon_timer_complete = 0;
|
||
|
|
||
|
/*
|
||
|
* Function to check if the the power key is pressed long enough.
|
||
|
* Return 0 if boot time more than PWRKEY_LONG_PRESS_COUNT.
|
||
|
* Return 1 if boot time less than PWRKEY_LONG_PRESS_COUNT.
|
||
|
*/
|
||
|
static uint32_t is_pwrkey_time_expired()
|
||
|
{
|
||
|
/* power on button tied in with PMIC KPDPWR. */
|
||
|
uint32_t sclk_count = platform_get_sclk_count();
|
||
|
|
||
|
/* Here check if the long press power-key lasts for 1.5s */
|
||
|
if (sclk_count > PWRKEY_LONG_PRESS_COUNT)
|
||
|
return 0;
|
||
|
else
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Function to check if the power on reason is power key triggered.
|
||
|
* Return 1 if it is triggered by power key.
|
||
|
* Return 0 if it is not triggered by power key.
|
||
|
*/
|
||
|
static uint32_t is_pwrkey_pon_reason()
|
||
|
{
|
||
|
#if PMI_CONFIGURED
|
||
|
return target_is_pwrkey_pon_reason();
|
||
|
#else
|
||
|
uint8_t pon_reason = pm8x41_get_pon_reason();
|
||
|
|
||
|
if (pm8x41_get_is_cold_boot() && (pon_reason == KPDPWR_N))
|
||
|
return 1;
|
||
|
else
|
||
|
return 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Main timer handle: check every PWRKEY_DETECT_FREQUENCY to see
|
||
|
* if power key is pressed.
|
||
|
* Shutdown the device if power key is release before
|
||
|
* (PWRKEY_LONG_PRESS_COUNT/MPM_SLEEP_TIMETICK_COUNT) seconds.
|
||
|
*/
|
||
|
static enum handler_return long_press_pwrkey_timer_func(struct timer *p_timer,
|
||
|
void *arg)
|
||
|
{
|
||
|
uint32_t sclk_count = platform_get_sclk_count();
|
||
|
|
||
|
/*
|
||
|
* The following condition is treated as the power key
|
||
|
* is pressed long enough.
|
||
|
* 1. if the power key is pressed last for PWRKEY_LONG_PRESS_COUNT.
|
||
|
*/
|
||
|
if (sclk_count > PWRKEY_LONG_PRESS_COUNT) {
|
||
|
timer_cancel(p_timer);
|
||
|
pon_timer_complete = 1;
|
||
|
} else {
|
||
|
if (pm8x41_get_pwrkey_is_pressed()) {
|
||
|
/*
|
||
|
* For normal man response is > 0.1 secs, so we use 0.05 secs default
|
||
|
* for software to be safely detect if there is a key release action.
|
||
|
*/
|
||
|
timer_set_oneshot(p_timer, PWRKEY_DETECT_FREQUENCY,
|
||
|
(timer_callback)long_press_pwrkey_timer_func, NULL);
|
||
|
} else {
|
||
|
shutdown_device();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return INT_RESCHEDULE;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Function to wait until the power key is pressed long enough
|
||
|
*/
|
||
|
static void wait_for_long_pwrkey_pressed()
|
||
|
{
|
||
|
uint32_t sclk_count = 0;
|
||
|
|
||
|
while (!pon_timer_complete) {
|
||
|
sclk_count = platform_get_sclk_count();
|
||
|
if (sclk_count > PWRKEY_LONG_PRESS_COUNT) {
|
||
|
timer_cancel(&pon_timer);
|
||
|
break;
|
||
|
}
|
||
|
thread_sleep(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Function to support for shutdown detection
|
||
|
* If below condition is met, the function will shut down
|
||
|
* the device. Otherwise it will do nothing and return to
|
||
|
* normal boot.
|
||
|
* condition:
|
||
|
* 1. it is triggered by power key &&
|
||
|
* 2. the power key is released before
|
||
|
* (PWRKEY_LONG_PRESS_COUNT/MPM_SLEEP_TIMETICK_COUNT) seconds.
|
||
|
*/
|
||
|
void shutdown_detect()
|
||
|
{
|
||
|
/*
|
||
|
* If it is booted by power key tirigger.
|
||
|
* Initialize pon_timer and call long_press_pwrkey_timer_func
|
||
|
* function to check if the power key is last press long enough.
|
||
|
*/
|
||
|
if (is_pwrkey_pon_reason()) {
|
||
|
if(!pm8x41_get_pwrkey_is_pressed()){
|
||
|
shutdown_device();
|
||
|
}
|
||
|
timer_initialize(&pon_timer);
|
||
|
timer_set_oneshot(&pon_timer, 0,(timer_callback)long_press_pwrkey_timer_func, NULL);
|
||
|
|
||
|
/*
|
||
|
* Wait until long press power key timeout
|
||
|
*
|
||
|
* It will be confused to end users if we shutdown the device
|
||
|
* after the splash screen displayed. But it can be moved the
|
||
|
* wait here if the boot time is much more considered.
|
||
|
*/
|
||
|
wait_for_long_pwrkey_pressed();
|
||
|
}
|
||
|
}
|