444 lines
10 KiB
C
444 lines
10 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* Copyright (C) 2021 MediaTek Inc.
|
|
*/
|
|
#ifdef pr_fmt
|
|
#undef pr_fmt
|
|
#endif
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
#include <linux/delay.h>
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/rtc.h>
|
|
#include <mach/upmu_hw.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/types.h>
|
|
#include <linux/sched/clock.h>
|
|
|
|
#include <mtk_rtc_hal_common.h>
|
|
#include "mtk_rtc_hw.h"
|
|
#include <mach/mtk_pmic_wrap.h>
|
|
#include <mtk_boot.h>
|
|
|
|
#include "include/pmic.h"
|
|
|
|
#define hal_rtc_xinfo(fmt, args...) \
|
|
pr_notice(fmt, ##args)
|
|
|
|
/*TODO extern bool pmic_chrdet_status(void);*/
|
|
|
|
/*
|
|
* RTC_FGSOC = 0,
|
|
* RTC_ANDROID,
|
|
* RTC_RECOVERY,
|
|
* RTC_FAC_RESET,
|
|
* RTC_BYPASS_PWR,
|
|
* RTC_PWRON_TIME,
|
|
* RTC_FAST_BOOT,
|
|
* RTC_KPOC,
|
|
* RTC_DEBUG,
|
|
* RTC_PWRON_AL,
|
|
* RTC_UART,
|
|
* RTC_AUTOBOOT,
|
|
* RTC_PWRON_LOGO,
|
|
* RTC_32K_LESS,
|
|
* RTC_LP_DET,
|
|
* RTC_SPAR_NUM
|
|
*
|
|
*/
|
|
/*
|
|
* RTC_PDN1:
|
|
* bit 0 - 3 : Android bits
|
|
* bit 4 - 5 : Recovery bits (0x10: factory data reset)
|
|
* bit 6 : Bypass PWRKEY bit
|
|
* bit 7 : Power-On Time bit
|
|
* bit 8 : RTC_GPIO_USER_WIFI bit
|
|
* bit 9 : RTC_GPIO_USER_GPS bit
|
|
* bit 10 : RTC_GPIO_USER_BT bit
|
|
* bit 11 : RTC_GPIO_USER_FM bit
|
|
* bit 12 : RTC_GPIO_USER_PMIC bit
|
|
* bit 13 : Fast Boot
|
|
* bit 14 : Kernel Power Off Charging
|
|
* bit 15 : Debug bit
|
|
*/
|
|
/*
|
|
* RTC_PDN2:
|
|
* bit 0 - 3 : MTH in power-on time
|
|
* bit 4 : Power-On Alarm bit
|
|
* bit 5 - 6 : UART bits
|
|
* bit 7 : autoboot bit
|
|
* bit 8 - 14: YEA in power-on time
|
|
* bit 15 : Power-On Logo bit
|
|
*/
|
|
/*
|
|
* RTC_SPAR0:
|
|
* bit 0 - 5 : SEC in power-on time
|
|
* bit 6 : 32K less bit. True:with 32K, False:Without 32K
|
|
* bit 7 - 15: reserved bits
|
|
*/
|
|
|
|
u16 rtc_spare_reg[RTC_SPAR_NUM][3] = {
|
|
{RTC_AL_MTH, 0xff, 8},
|
|
{RTC_PDN1, 0xf, 0},
|
|
{RTC_PDN1, 0x3, 4},
|
|
{RTC_PDN1, 0x1, 6},
|
|
{RTC_PDN1, 0x1, 7},
|
|
{RTC_PDN1, 0x1, 13},
|
|
{RTC_PDN1, 0x1, 14},
|
|
{RTC_PDN1, 0x1, 15},
|
|
{RTC_PDN2, 0x1, 4},
|
|
{RTC_PDN2, 0x3, 5},
|
|
{RTC_PDN2, 0x1, 7},
|
|
{RTC_PDN2, 0x1, 15},
|
|
{RTC_SPAR0, 0x1, 6},
|
|
{RTC_SPAR0, 0x1, 7},
|
|
{RTC_AL_HOU, 0xff, 8}
|
|
};
|
|
|
|
static int rtc_eosc_cali_td = 8;
|
|
module_param(rtc_eosc_cali_td, int, 0664);
|
|
|
|
void hal_rtc_set_abb_32k(u16 enable)
|
|
{
|
|
hal_rtc_xinfo("ABB 32k not support\n");
|
|
}
|
|
|
|
u16 hal_rtc_get_gpio_32k_status(void)
|
|
{
|
|
u16 con;
|
|
|
|
con = rtc_read(RTC_CON);
|
|
|
|
hal_rtc_xinfo("RTC_GPIO 32k status(RTC_CON=0x%x)\n", con);
|
|
|
|
if (con & RTC_CON_F32KOB)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
void hal_rtc_set_gpio_32k_status(u16 user, bool enable)
|
|
{
|
|
u16 con, pdn1;
|
|
|
|
if (enable) {
|
|
pdn1 = rtc_read(RTC_PDN1);
|
|
} else {
|
|
pdn1 = rtc_read(RTC_PDN1) & ~(1U << user);
|
|
rtc_write(RTC_PDN1, pdn1);
|
|
rtc_write_trigger();
|
|
}
|
|
|
|
con = rtc_read(RTC_CON);
|
|
if (enable) {
|
|
con &= ~RTC_CON_F32KOB;
|
|
} else {
|
|
if (!(pdn1 & RTC_GPIO_USER_MASK)) { /* no users */
|
|
con |= RTC_CON_F32KOB;
|
|
}
|
|
}
|
|
rtc_write(RTC_CON, con);
|
|
rtc_write_trigger();
|
|
|
|
|
|
if (enable) {
|
|
pdn1 |= (1U << user);
|
|
rtc_write(RTC_PDN1, pdn1);
|
|
rtc_write_trigger();
|
|
}
|
|
hal_rtc_xinfo("RTC_GPIO user %d enable = %d 32k (0x%x), RTC_CON = %x\n",
|
|
user, enable, pdn1, rtc_read(RTC_CON));
|
|
}
|
|
|
|
void rtc_spar_alarm_clear_wait(void)
|
|
{
|
|
unsigned long long timeout = sched_clock() + 500000000;
|
|
|
|
do {
|
|
if ((rtc_read(RTC_BBPU) & RTC_BBPU_CLR) == 0)
|
|
break;
|
|
else if (sched_clock() > timeout) {
|
|
pr_notice("%s, spar/alarm clear time out, %x,\n",
|
|
__func__, rtc_read(RTC_BBPU));
|
|
break;
|
|
}
|
|
} while (1);
|
|
}
|
|
|
|
void rtc_enable_k_eosc(void)
|
|
{
|
|
u16 osc32;
|
|
/* Truning on eosc cali mode clock */
|
|
pmic_config_interface_nolock(PMIC_SCK_TOP_CKPDN_CON0_CLR_ADDR,
|
|
1, PMIC_RG_RTC_EOSC32_CK_PDN_MASK,
|
|
PMIC_RG_RTC_EOSC32_CK_PDN_SHIFT);
|
|
pmic_config_interface_nolock(PMIC_RG_SRCLKEN_IN0_HW_MODE_ADDR,
|
|
1, PMIC_RG_SRCLKEN_IN0_HW_MODE_MASK,
|
|
PMIC_RG_SRCLKEN_IN0_HW_MODE_SHIFT);
|
|
pmic_config_interface_nolock(PMIC_RG_SRCLKEN_IN1_HW_MODE_ADDR,
|
|
1, PMIC_RG_SRCLKEN_IN1_HW_MODE_MASK,
|
|
PMIC_RG_SRCLKEN_IN1_HW_MODE_SHIFT);
|
|
pmic_config_interface_nolock(PMIC_RG_RTC_EOSC32_CK_PDN_ADDR, 0,
|
|
PMIC_RG_RTC_EOSC32_CK_PDN_MASK,
|
|
PMIC_RG_RTC_EOSC32_CK_PDN_SHIFT);
|
|
|
|
switch (rtc_eosc_cali_td) {
|
|
case 1:
|
|
pmic_config_interface_nolock(PMIC_EOSC_CALI_TD_ADDR, 0x3,
|
|
PMIC_EOSC_CALI_TD_MASK, PMIC_EOSC_CALI_TD_SHIFT);
|
|
break;
|
|
case 2:
|
|
pmic_config_interface_nolock(PMIC_EOSC_CALI_TD_ADDR, 0x4,
|
|
PMIC_EOSC_CALI_TD_MASK, PMIC_EOSC_CALI_TD_SHIFT);
|
|
break;
|
|
case 4:
|
|
pmic_config_interface_nolock(PMIC_EOSC_CALI_TD_ADDR, 0x5,
|
|
PMIC_EOSC_CALI_TD_MASK, PMIC_EOSC_CALI_TD_SHIFT);
|
|
break;
|
|
case 16:
|
|
pmic_config_interface_nolock(PMIC_EOSC_CALI_TD_ADDR, 0x7,
|
|
PMIC_EOSC_CALI_TD_MASK, PMIC_EOSC_CALI_TD_SHIFT);
|
|
break;
|
|
default:
|
|
pmic_config_interface_nolock(PMIC_EOSC_CALI_TD_ADDR, 0x6,
|
|
PMIC_EOSC_CALI_TD_MASK, PMIC_EOSC_CALI_TD_SHIFT);
|
|
break;
|
|
}
|
|
/*Switch the DCXO from 32k-less mode to RTC mode, otherwise,
|
|
* EOSC cali will fail
|
|
*/
|
|
/*RTC mode will have only OFF mode and FPM */
|
|
pmic_config_interface_nolock(PMIC_XO_EN32K_MAN_ADDR,
|
|
0, PMIC_XO_EN32K_MAN_MASK,
|
|
PMIC_XO_EN32K_MAN_SHIFT);
|
|
rtc_write(RTC_BBPU,
|
|
rtc_read(RTC_BBPU) | RTC_BBPU_KEY | RTC_BBPU_RELOAD);
|
|
rtc_write_trigger();
|
|
/* Enable K EOSC mode for normal power off and then plug out battery */
|
|
rtc_write(RTC_AL_YEA, ((rtc_read(RTC_AL_YEA) | RTC_K_EOSC_RSV_0)
|
|
& (~RTC_K_EOSC_RSV_1)) | RTC_K_EOSC_RSV_2);
|
|
rtc_write_trigger();
|
|
|
|
osc32 = rtc_read(RTC_OSC32CON);
|
|
rtc_xosc_write(osc32 | RTC_EMBCK_SRC_SEL, true);
|
|
hal_rtc_xinfo("RTC_enable_k_eosc\n");
|
|
}
|
|
|
|
void rtc_disable_2sec_reboot(void)
|
|
{
|
|
u16 reboot;
|
|
|
|
reboot = (rtc_read(RTC_AL_SEC) & ~RTC_BBPU_2SEC_EN)
|
|
& ~RTC_BBPU_AUTO_PDN_SEL;
|
|
rtc_write(RTC_AL_SEC, reboot);
|
|
rtc_write_trigger();
|
|
}
|
|
|
|
void rtc_bbpu_pwrdown(bool auto_boot)
|
|
{
|
|
PMIC_POWER_HOLD(0);
|
|
}
|
|
|
|
void hal_rtc_bbpu_pwdn(bool charger_status)
|
|
{
|
|
u16 con, bbpu;
|
|
|
|
rtc_disable_2sec_reboot();
|
|
rtc_enable_k_eosc();
|
|
|
|
/* disable 32K export if there are no RTC_GPIO users */
|
|
if (!(rtc_read(RTC_PDN1) & RTC_GPIO_USER_MASK)) {
|
|
con = rtc_read(RTC_CON) | RTC_CON_F32KOB;
|
|
rtc_write(RTC_CON, con);
|
|
rtc_write_trigger();
|
|
}
|
|
/* lpsd */
|
|
pr_notice("clear lpsd solution\n");
|
|
bbpu = RTC_BBPU_KEY | RTC_BBPU_CLR | RTC_BBPU_PWREN;
|
|
rtc_write(RTC_BBPU, bbpu);
|
|
|
|
rtc_write(RTC_AL_MASK, RTC_AL_MASK_DOW); /* mask DOW */
|
|
rtc_write_trigger();
|
|
|
|
rtc_spar_alarm_clear_wait();
|
|
|
|
wk_pmic_enable_sdn_delay();
|
|
|
|
rtc_write(RTC_BBPU,
|
|
rtc_read(RTC_BBPU) | RTC_BBPU_KEY | RTC_BBPU_RELOAD);
|
|
rtc_write_trigger();
|
|
pr_notice("RTC_AL_MASK= 0x%x RTC_IRQ_EN= 0x%x\n",
|
|
rtc_read(RTC_AL_MASK), rtc_read(RTC_IRQ_EN));
|
|
/* lpsd */
|
|
rtc_bbpu_pwrdown(true);
|
|
}
|
|
|
|
void hal_rtc_get_pwron_alarm(struct rtc_time *tm, struct rtc_wkalrm *alm)
|
|
{
|
|
u16 pdn1, pdn2;
|
|
|
|
|
|
pdn1 = rtc_read(RTC_PDN1);
|
|
pdn2 = rtc_read(RTC_PDN2);
|
|
|
|
alm->enabled = (pdn1 & RTC_PDN1_PWRON_TIME ?
|
|
(pdn2 & RTC_PDN2_PWRON_LOGO ? 3 : 2) : 0);
|
|
/* return Power-On Alarm bit */
|
|
alm->pending = !!(pdn2 & RTC_PDN2_PWRON_ALARM);
|
|
|
|
hal_rtc_get_alarm_time(tm);
|
|
}
|
|
|
|
bool hal_rtc_is_lp_irq(void)
|
|
{
|
|
u16 irqsta;
|
|
|
|
irqsta = rtc_read(RTC_IRQ_STA); /* read clear */
|
|
if (unlikely(!(irqsta & RTC_IRQ_STA_AL))) {
|
|
#ifndef USER_BUILD_KERNEL
|
|
if (irqsta & RTC_IRQ_STA_LP)
|
|
rtc_lp_exception();
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool hal_rtc_is_pwron_alarm(struct rtc_time *nowtm, struct rtc_time *tm)
|
|
{
|
|
u16 pdn1;
|
|
|
|
pdn1 = rtc_read(RTC_PDN1);
|
|
hal_rtc_xinfo("pdn1 = 0x%4x\n", pdn1);
|
|
|
|
if (pdn1 & RTC_PDN1_PWRON_TIME) { /* power-on time is available */
|
|
|
|
hal_rtc_xinfo("pdn1 = 0x%4x\n", pdn1);
|
|
hal_rtc_get_tick_time(nowtm);
|
|
hal_rtc_xinfo("pdn1 = 0x%4x\n", pdn1);
|
|
/* SEC has carried */
|
|
if (rtc_read(RTC_TC_SEC) < nowtm->tm_sec)
|
|
hal_rtc_get_tick_time(nowtm);
|
|
|
|
hal_rtc_get_pwron_alarm_time(tm);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void hal_rtc_get_alarm(struct rtc_time *tm, struct rtc_wkalrm *alm)
|
|
{
|
|
u16 irqen, pdn2;
|
|
|
|
irqen = rtc_read(RTC_IRQ_EN);
|
|
hal_rtc_get_alarm_time(tm);
|
|
pdn2 = rtc_read(RTC_PDN2);
|
|
alm->enabled = !!(irqen & RTC_IRQ_EN_AL);
|
|
/* return Power-On Alarm bit */
|
|
alm->pending = !!(pdn2 & RTC_PDN2_PWRON_ALARM);
|
|
}
|
|
|
|
void hal_rtc_set_alarm(struct rtc_time *tm)
|
|
{
|
|
u16 irqen;
|
|
|
|
hal_rtc_set_alarm_time(tm);
|
|
|
|
irqen = rtc_read(RTC_IRQ_EN) | RTC_IRQ_EN_ONESHOT_AL;
|
|
rtc_write(RTC_IRQ_EN, irqen);
|
|
rtc_write_trigger();
|
|
}
|
|
|
|
void hal_rtc_clear_alarm(struct rtc_time *tm)
|
|
{
|
|
u16 irqsta, irqen, pdn2;
|
|
|
|
irqen = rtc_read(RTC_IRQ_EN) & ~RTC_IRQ_EN_AL;
|
|
pdn2 = rtc_read(RTC_PDN2) & ~RTC_PDN2_PWRON_ALARM;
|
|
rtc_write(RTC_IRQ_EN, irqen);
|
|
rtc_write(RTC_PDN2, pdn2);
|
|
rtc_write_trigger();
|
|
irqsta = rtc_read(RTC_IRQ_STA); /* read clear */
|
|
|
|
hal_rtc_set_alarm_time(tm);
|
|
}
|
|
|
|
void hal_rtc_set_lp_irq(void)
|
|
{
|
|
u16 irqen;
|
|
|
|
#ifndef USER_BUILD_KERNEL
|
|
irqen = rtc_read(RTC_IRQ_EN) | RTC_IRQ_EN_LP;
|
|
#else
|
|
irqen = rtc_read(RTC_IRQ_EN) & ~RTC_IRQ_EN_LP;
|
|
#endif
|
|
rtc_write(RTC_IRQ_EN, irqen);
|
|
rtc_write_trigger();
|
|
}
|
|
|
|
void hal_rtc_save_pwron_time(bool enable, struct rtc_time *tm, bool logo)
|
|
{
|
|
u16 pdn1, pdn2;
|
|
|
|
hal_rtc_set_pwron_alarm_time(tm);
|
|
|
|
if (logo)
|
|
pdn2 = rtc_read(RTC_PDN2) | RTC_PDN2_PWRON_LOGO;
|
|
else
|
|
pdn2 = rtc_read(RTC_PDN2) & ~RTC_PDN2_PWRON_LOGO;
|
|
|
|
rtc_write(RTC_PDN2, pdn2);
|
|
|
|
if (enable)
|
|
pdn1 = rtc_read(RTC_PDN1) | RTC_PDN1_PWRON_TIME;
|
|
else
|
|
pdn1 = rtc_read(RTC_PDN1) & ~RTC_PDN1_PWRON_TIME;
|
|
rtc_write(RTC_PDN1, pdn1);
|
|
rtc_write_trigger();
|
|
}
|
|
|
|
void rtc_clock_enable(int enable)
|
|
{
|
|
if (enable) {
|
|
pmic_config_interface_nolock(PMIC_SCK_TOP_CKPDN_CON0_CLR_ADDR,
|
|
1,
|
|
PMIC_RG_RTC_MCLK_PDN_MASK,
|
|
PMIC_RG_RTC_MCLK_PDN_SHIFT);
|
|
pmic_config_interface_nolock(PMIC_SCK_TOP_CKPDN_CON0_CLR_ADDR,
|
|
1,
|
|
PMIC_RG_RTC_32K_CK_PDN_MASK,
|
|
PMIC_RG_RTC_32K_CK_PDN_SHIFT);
|
|
} else {
|
|
pmic_config_interface_nolock(PMIC_SCK_TOP_CKPDN_CON0_SET_ADDR,
|
|
1,
|
|
PMIC_RG_RTC_MCLK_PDN_MASK,
|
|
PMIC_RG_RTC_MCLK_PDN_SHIFT);
|
|
pmic_config_interface_nolock(PMIC_SCK_TOP_CKPDN_CON0_SET_ADDR,
|
|
1,
|
|
PMIC_RG_RTC_32K_CK_PDN_MASK,
|
|
PMIC_RG_RTC_32K_CK_PDN_SHIFT);
|
|
}
|
|
}
|
|
|
|
void rtc_lpsd_restore_al_mask(void)
|
|
{
|
|
pr_notice("%s\n", __func__);
|
|
|
|
rtc_write(RTC_BBPU,
|
|
rtc_read(RTC_BBPU) | RTC_BBPU_KEY | RTC_BBPU_RELOAD);
|
|
rtc_write_trigger();
|
|
pr_notice("1st RTC_AL_MASK = 0x%x\n", rtc_read(RTC_AL_MASK));
|
|
/* mask DOW */
|
|
rtc_write(RTC_AL_MASK, RTC_AL_MASK_DOW);
|
|
rtc_write_trigger();
|
|
pr_notice("2nd RTC_AL_MASK = 0x%x\n", rtc_read(RTC_AL_MASK));
|
|
}
|