unplugged-kernel/drivers/misc/mediatek/rtc/mtk_rtc_hal_common.c

338 lines
8.4 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2021 MediaTek Inc.
*/
#if defined(CONFIG_MTK_RTC)
#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.h>
#include <mtk_rtc_hw.h>
#include <mtk_rtc_hal_common.h>
#ifdef CONFIG_MTK_PMIC_WRAP_HAL
#include <mach/mtk_pmic_wrap.h>
#endif
#define hal_rtc_xinfo(fmt, args...) \
pr_notice(fmt, ##args)
u16 rtc_read(u16 addr)
{
u32 rdata = 0;
#ifdef CONFIG_MTK_PMIC_WRAP_HAL
pwrap_read((u32) addr, &rdata);
#endif
return (u16) rdata;
}
void rtc_write(u16 addr, u16 data)
{
#ifdef CONFIG_MTK_PMIC_WRAP_HAL
pwrap_write((u32) addr, (u32) data);
#endif
}
void rtc_busy_wait(void)
{
unsigned long long timeout = sched_clock() + 500000000;
do {
if ((rtc_read(RTC_BBPU) & RTC_BBPU_CBUSY) == 0)
break;
else if (sched_clock() > timeout) {
pr_err("%s, wait cbusy timeout, %x, %x, %x, %d\n",
__func__,
rtc_read(RTC_BBPU), rtc_read(RTC_POWERKEY1),
rtc_read(RTC_POWERKEY2), rtc_read(RTC_TC_SEC));
break;
}
} while (1);
}
void rtc_write_trigger(void)
{
rtc_write(RTC_WRTGR, 1);
rtc_busy_wait();
}
void rtc_writeif_unlock(void)
{
rtc_write(RTC_PROT, RTC_PROT_UNLOCK1);
rtc_write_trigger();
rtc_write(RTC_PROT, RTC_PROT_UNLOCK2);
rtc_write_trigger();
}
void rtc_xosc_write(u16 val, bool reload)
{
rtc_write(RTC_OSC32CON, RTC_OSC32CON_UNLOCK1);
rtc_busy_wait();
rtc_write(RTC_OSC32CON, RTC_OSC32CON_UNLOCK2);
rtc_busy_wait();
rtc_write(RTC_OSC32CON, val);
rtc_busy_wait();
if (reload) {
u16 bbpu;
bbpu = rtc_read(RTC_BBPU) | RTC_BBPU_KEY | RTC_BBPU_RELOAD;
rtc_write(RTC_BBPU, bbpu);
rtc_write_trigger();
}
}
void rtc_set_writeif(bool enable)
{
if (enable) {
rtc_writeif_unlock();
} else {
rtc_write(RTC_PROT, 0);
rtc_write_trigger();
}
}
void hal_rtc_set_spare_register(enum rtc_spare_enum cmd, u16 val)
{
u16 tmp_val;
if (cmd >= 0 && cmd < RTC_SPAR_NUM) {
hal_rtc_xinfo("%s: cmd[%d], set rg[0x%x, 0x%x , %d] = 0x%x\n",
__func__, cmd,
rtc_spare_reg[cmd][RTC_REG],
rtc_spare_reg[cmd][RTC_MASK],
rtc_spare_reg[cmd][RTC_SHIFT], val);
tmp_val =
rtc_read(rtc_spare_reg[cmd][RTC_REG]) &
~(rtc_spare_reg[cmd][RTC_MASK] <<
rtc_spare_reg[cmd][RTC_SHIFT]);
rtc_write(rtc_spare_reg[cmd][RTC_REG],
tmp_val | ((val & rtc_spare_reg[cmd][RTC_MASK]) <<
rtc_spare_reg[cmd][RTC_SHIFT]));
rtc_write_trigger();
}
}
u16 hal_rtc_get_spare_register(enum rtc_spare_enum cmd)
{
u16 tmp_val;
if (cmd >= 0 && cmd < RTC_SPAR_NUM) {
tmp_val = rtc_read(rtc_spare_reg[cmd][RTC_REG]);
tmp_val =
(tmp_val >> rtc_spare_reg[cmd][RTC_SHIFT]) &
rtc_spare_reg[cmd][RTC_MASK];
hal_rtc_xinfo("%s: cmd[%d], get rg[0x%x, 0x%x , %d] = 0x%x\n",
__func__, cmd,
rtc_spare_reg[cmd][RTC_REG],
rtc_spare_reg[cmd][RTC_MASK],
rtc_spare_reg[cmd][RTC_SHIFT], tmp_val);
return tmp_val;
}
return 0;
}
static void rtc_get_tick(struct rtc_time *tm)
{
tm->tm_sec = rtc_read(RTC_TC_SEC);
tm->tm_min = rtc_read(RTC_TC_MIN);
tm->tm_hour = rtc_read(RTC_TC_HOU);
tm->tm_mday = rtc_read(RTC_TC_DOM);
tm->tm_mon = rtc_read(RTC_TC_MTH) & RTC_TC_MTH_MASK;
tm->tm_year = rtc_read(RTC_TC_YEA);
}
void hal_rtc_get_tick_time(struct rtc_time *tm)
{
u16 bbpu;
bbpu = rtc_read(RTC_BBPU) | RTC_BBPU_KEY | RTC_BBPU_RELOAD;
rtc_write(RTC_BBPU, bbpu);
rtc_write_trigger();
rtc_get_tick(tm);
bbpu = rtc_read(RTC_BBPU) | RTC_BBPU_KEY | RTC_BBPU_RELOAD;
rtc_write(RTC_BBPU, bbpu);
rtc_write_trigger();
if (rtc_read(RTC_TC_SEC) < tm->tm_sec) { /* SEC has carried */
rtc_get_tick(tm);
}
}
void hal_rtc_set_tick_time(struct rtc_time *tm)
{
rtc_write(RTC_TC_YEA, tm->tm_year);
rtc_write(RTC_TC_MTH, tm->tm_mon);
rtc_write(RTC_TC_DOM, tm->tm_mday);
rtc_write(RTC_TC_HOU, tm->tm_hour);
rtc_write(RTC_TC_MIN, tm->tm_min);
rtc_write(RTC_TC_SEC, tm->tm_sec);
rtc_write_trigger();
}
void hal_rtc_get_alarm_time(struct rtc_time *tm)
{
tm->tm_sec = rtc_read(RTC_AL_SEC) & RTC_AL_SEC_MASK;
tm->tm_min = rtc_read(RTC_AL_MIN) & RTC_AL_MIN_MASK;
tm->tm_hour = rtc_read(RTC_AL_HOU) & RTC_AL_HOU_MASK;
tm->tm_mday = rtc_read(RTC_AL_DOM) & RTC_AL_DOM_MASK;
tm->tm_mon = rtc_read(RTC_AL_MTH) & RTC_AL_MTH_MASK;
tm->tm_year = rtc_read(RTC_AL_YEA) & RTC_AL_YEA_MASK;
}
void hal_rtc_set_alarm_time(struct rtc_time *tm)
{
hal_rtc_xinfo("mon = %d, day = %d, hour = %d\n",
(rtc_read(RTC_AL_MTH) & ~(RTC_AL_MTH_MASK)) | tm->tm_mon,
(rtc_read(RTC_AL_DOM) & ~(RTC_AL_DOM_MASK)) | tm->tm_mday,
(rtc_read(RTC_AL_HOU) & ~(RTC_AL_HOU_MASK)) | tm->tm_hour);
rtc_write(RTC_AL_YEA,
(rtc_read(RTC_AL_YEA) & ~(RTC_AL_YEA_MASK)) |
(tm->tm_year & RTC_AL_YEA_MASK));
rtc_write(RTC_AL_MTH,
(rtc_read(RTC_AL_MTH) & ~(RTC_AL_MTH_MASK)) |
(tm->tm_mon & RTC_AL_MTH_MASK));
rtc_write(RTC_AL_DOM,
(rtc_read(RTC_AL_DOM) & ~(RTC_AL_DOM_MASK)) |
(tm->tm_mday & RTC_AL_DOM_MASK));
rtc_write(RTC_AL_HOU,
(rtc_read(RTC_AL_HOU) & ~(RTC_AL_HOU_MASK)) |
(tm->tm_hour & RTC_AL_HOU_MASK));
rtc_write(RTC_AL_MIN,
(rtc_read(RTC_AL_MIN) & ~(RTC_AL_MIN_MASK)) |
(tm->tm_min & RTC_AL_MIN_MASK));
rtc_write(RTC_AL_SEC,
(rtc_read(RTC_AL_SEC) & ~(RTC_AL_SEC_MASK)) |
(tm->tm_sec & RTC_AL_SEC_MASK));
rtc_write(RTC_AL_MASK, RTC_AL_MASK_DOW); /* mask DOW */
rtc_write_trigger();
}
void hal_rtc_save_pwron_alarm(void)
{
rtc_write(RTC_PDN1, rtc_read(RTC_PDN1) & (~RTC_PDN1_PWRON_TIME));
rtc_write(RTC_PDN2, rtc_read(RTC_PDN2) | RTC_PDN2_PWRON_ALARM);
rtc_write_trigger();
}
void hal_rtc_get_pwron_alarm_time(struct rtc_time *tm)
{
tm->tm_sec =
(rtc_read(RTC_PWRON_SEC) & RTC_PWRON_SEC_MASK) >>
RTC_PWRON_SEC_SHIFT;
tm->tm_min =
(rtc_read(RTC_PWRON_MIN) & RTC_PWRON_MIN_MASK) >>
RTC_PWRON_MIN_SHIFT;
tm->tm_hour =
(rtc_read(RTC_PWRON_HOU) & RTC_PWRON_HOU_MASK) >>
RTC_PWRON_HOU_SHIFT;
tm->tm_mday =
(rtc_read(RTC_PWRON_DOM) & RTC_PWRON_DOM_MASK) >>
RTC_PWRON_DOM_SHIFT;
tm->tm_mon =
(rtc_read(RTC_PWRON_MTH) & RTC_PWRON_MTH_MASK) >>
RTC_PWRON_MTH_SHIFT;
tm->tm_year =
(rtc_read(RTC_PWRON_YEA) & RTC_PWRON_YEA_MASK) >>
RTC_PWRON_YEA_SHIFT;
}
void hal_rtc_set_pwron_alarm_time(struct rtc_time *tm)
{
rtc_write(RTC_PWRON_YEA,
(rtc_read(RTC_PWRON_YEA) & ~(RTC_PWRON_YEA_MASK))
| ((tm->tm_year << RTC_PWRON_YEA_SHIFT) &
RTC_PWRON_YEA_MASK));
rtc_write_trigger();
rtc_write(RTC_PWRON_MTH,
(rtc_read(RTC_PWRON_MTH) & ~(RTC_PWRON_MTH_MASK))
| ((tm->tm_mon << RTC_PWRON_MTH_SHIFT) & RTC_PWRON_MTH_MASK));
rtc_write_trigger();
rtc_write(RTC_PWRON_DOM,
(rtc_read(RTC_PWRON_DOM) & ~(RTC_PWRON_DOM_MASK))
| ((tm->tm_mday << RTC_PWRON_DOM_SHIFT) &
RTC_PWRON_DOM_MASK));
rtc_write_trigger();
rtc_write(RTC_PWRON_HOU,
(rtc_read(RTC_PWRON_HOU) & ~(RTC_PWRON_HOU_MASK))
| ((tm->tm_hour << RTC_PWRON_HOU_SHIFT) &
RTC_PWRON_HOU_MASK));
rtc_write_trigger();
rtc_write(RTC_PWRON_MIN,
(rtc_read(RTC_PWRON_MIN) & ~(RTC_PWRON_MIN_MASK))
| ((tm->tm_min << RTC_PWRON_MIN_SHIFT) & RTC_PWRON_MIN_MASK));
rtc_write_trigger();
rtc_write(RTC_PWRON_SEC,
(rtc_read(RTC_PWRON_SEC) & ~(RTC_PWRON_SEC_MASK))
| ((tm->tm_sec << RTC_PWRON_SEC_SHIFT) & RTC_PWRON_SEC_MASK));
rtc_write_trigger();
}
void hal_rtc_read_rg(void)
{
u16 irqen, pdn1;
irqen = rtc_read(RTC_IRQ_EN);
pdn1 = rtc_read(RTC_PDN1);
hal_rtc_xinfo("RTC_IRQ_EN = 0x%x, RTC_PDN1 = 0x%x\n", irqen, pdn1);
}
#ifndef USER_BUILD_KERNEL
void rtc_lp_exception(void)
{
u16 bbpu, irqsta, irqen, osc32;
u16 pwrkey1, pwrkey2, prot, con, sec1, sec2;
bbpu = rtc_read(RTC_BBPU);
irqsta = rtc_read(RTC_IRQ_STA);
irqen = rtc_read(RTC_IRQ_EN);
osc32 = rtc_read(RTC_OSC32CON);
pwrkey1 = rtc_read(RTC_POWERKEY1);
pwrkey2 = rtc_read(RTC_POWERKEY2);
prot = rtc_read(RTC_PROT);
con = rtc_read(RTC_CON);
sec1 = rtc_read(RTC_TC_SEC);
mdelay(2000);
sec2 = rtc_read(RTC_TC_SEC);
pr_emerg("!!! 32K WAS STOPPED !!!\n"
"RTC_BBPU = 0x%x\n"
"RTC_IRQ_STA = 0x%x\n"
"RTC_IRQ_EN = 0x%x\n"
"RTC_OSC32CON = 0x%x\n"
"RTC_POWERKEY1 = 0x%x\n"
"RTC_POWERKEY2 = 0x%x\n"
"RTC_PROT = 0x%x\n"
"RTC_CON = 0x%x\n"
"RTC_TC_SEC = %02d\n"
"RTC_TC_SEC = %02d\n",
bbpu, irqsta, irqen, osc32, pwrkey1, pwrkey2, prot, con, sec1,
sec2);
}
#endif
#endif /*#if defined(CONFIG_MTK_RTC) */