[Feature] add poweralarm /wakup-alarm lib
Change-Id: Iff1697a5347d309e6d3ccbbcbf49f997abda76e4
diff --git a/src/bsp/lk/platform/mt2735/drivers/pmic/pmic.c b/src/bsp/lk/platform/mt2735/drivers/pmic/pmic.c
index 17280bd..c0e48de 100644
--- a/src/bsp/lk/platform/mt2735/drivers/pmic/pmic.c
+++ b/src/bsp/lk/platform/mt2735/drivers/pmic/pmic.c
@@ -412,7 +412,7 @@
pmic_read_interface(PMIC_PUP_PKEY_RELEASE_ADDR, &g_pwrkey_release,
PMIC_PUP_PKEY_RELEASE_MASK, PMIC_PUP_PKEY_RELEASE_SHIFT);
/*--UVLO off--*/
- dprintf(INFO, "[PMIC]TOP_RST_STATUS[0x%x]=0x%x\n",
+ dprintf(ALWAYS, "[PMIC]TOP_RST_STATUS[0x%x]=0x%x\n",
MT6330_TOP_RST_STATUS, upmu_get_reg_value(MT6330_TOP_RST_STATUS));
/*special for RTC Alarm and SPAR*/
pon_sts = upmu_get_reg_value(MT6330_PONSTS);
diff --git a/src/bsp/lk/platform/mt2735/drivers/rtc/rtc.c b/src/bsp/lk/platform/mt2735/drivers/rtc/rtc.c
index 5e14d1a..df570c8 100644
--- a/src/bsp/lk/platform/mt2735/drivers/rtc/rtc.c
+++ b/src/bsp/lk/platform/mt2735/drivers/rtc/rtc.c
@@ -1033,6 +1033,7 @@
Write_trigger();
}
#endif
+ RTC_LOG("Power on alarm\n");
return true;
} else if (now_time < time) { /* set power-on alarm */
RTC_Write(RTC_AL_YEA, (RTC_Read(RTC_AL_YEA) & (~RTC_AL_YEA_MASK)) | ((yea - RTC_MIN_YEAR) & RTC_AL_YEA_MASK));
@@ -1046,6 +1047,7 @@
irqen = RTC_Read(RTC_IRQ_EN) | RTC_IRQ_EN_ONESHOT_AL;
RTC_Write(RTC_IRQ_EN, irqen);
Write_trigger();
+ RTC_LOG("Not power on alarm\n");
} else {
pdn1 = (pdn1 & ~RTC_PDN1_PWRON_TIME);
#if defined (MTK_KERNEL_POWER_OFF_CHARGING)
@@ -1056,7 +1058,9 @@
Write_trigger();
RTC_LOG("Expired alarm\n");
}
- }
+ } else {
+ RTC_LOG("Not power on alarm\n");
+ }
}
if ((pdn1 & RTC_PDN1_RECOVERY_MASK) == RTC_PDN1_FAC_RESET) { /* factory data reset */
diff --git a/src/bsp/lk/platform/mt2735/drivers/wdt/mtk_wdt.c b/src/bsp/lk/platform/mt2735/drivers/wdt/mtk_wdt.c
index a2477ef..86e9d19 100644
--- a/src/bsp/lk/platform/mt2735/drivers/wdt/mtk_wdt.c
+++ b/src/bsp/lk/platform/mt2735/drivers/wdt/mtk_wdt.c
@@ -72,7 +72,50 @@
static uint32_t wdt_reset_status;
static uint8_t wdt_inited = 0U;
+static unsigned int rgu_mode;
+enum {
+ MTK_WDT_NONRST2_BOOT_MASK = 0xF,
+ MTK_WDT_NONRST2_BOOT_CHARGER = 1,
+ MTK_WDT_NONRST2_BOOT_RECOVERY = 2,
+ MTK_WDT_NONRST2_BOOT_BOOTLOADER = 3,
+ MTK_WDT_NONRST2_BOOT_DM_VERITY = 4,
+ MTK_WDT_NONRST2_BOOT_KPOC = 5,
+ MTK_WDT_NONRST2_BOOT_DDR_RSVD = 6,
+ MTK_WDT_NONRST2_BOOT_META = 7,
+ MTK_WDT_NONRST2_BOOT_RPMBPK = 8,
+ MTK_WDT_NONRST2_BYPASS_PWR_KEY = 1 << 13,
+ MTK_WDT_NONRST2_STAGE_OFS = 29,
+ MTK_WDT_NONRST2_LAST_STAGE_OFS = 26
+};
+
+static unsigned int mtk_wdt_get_status(void)
+{
+ static unsigned int wdt_sta = 0;
+ static unsigned int wdt_sta_handled = 0;
+ unsigned int reg;
+
+ /*
+ * Note:
+ * Because WDT_STA register will be cleared after writing WDT_MODE,
+ * we use a static variable to keep orinigal WDT_STA.
+ *
+ * After reset, static varialbe will always be clear to 0,
+ * so only read WDT_STA when static variable is 0 is OK
+ *
+ * Use "wdt_sta_handled" to indicate if WDT_STATUS is preserved.
+ * Do not use "wdt_sta" as indication because dummy handling will be
+ * executed in case WDT_STATUS is 0 originally.
+ */
+ if (wdt_sta_handled == 0) {
+
+ wdt_sta = wdt_readl(WDT_STA);
+
+ wdt_sta_handled = 1;
+ }
+
+ return wdt_sta;
+}
static inline void mtk_wdt_clear_all_status(void)
{
@@ -146,6 +189,21 @@
void mtk_wdt_init(void)
{
unsigned int wdt_ctrl;
+
+ rgu_mode = wdt_readl(WDT_MODE);
+
+ dprintf(ALWAYS, "MODE: 0x%x\n", wdt_readl(WDT_MODE));
+ dprintf(ALWAYS, "STA: 0x%x\n", mtk_wdt_get_status());
+ dprintf(ALWAYS, "LENGTH: 0x%x\n", wdt_readl(WDT_LENGTH));
+ dprintf(ALWAYS, "INTERVAL: 0x%x\n", wdt_readl(WDT_INTERNAL));
+ dprintf(ALWAYS, "SWSYSRST: 0x%x\n", wdt_readl(WDT_SWSYSRST));
+ dprintf(ALWAYS, "LATCH_CTL: 0x%x\n", wdt_readl(WDT_LATCH_CTL));
+ dprintf(ALWAYS, "NONRST_REG: 0x%x\n", wdt_readl(WDT_NONRST_REG));
+ dprintf(ALWAYS, "NONRST_REG2: 0x%x\n", wdt_readl(WDT_NONRST_REG2));
+
+ if (wdt_readl(WDT_NONRST_REG2) & MTK_WDT_NONRST2_BYPASS_PWR_KEY)
+ rgu_mode |= MTK_WDT_NONRST2_BYPASS_PWR_KEY;
+
mtk_wdt_set_default_mode(WDT_DISABLE, WDT_TIMEOUT_LK_SEC);
if (wdt_inited == 0U) {
@@ -317,6 +375,44 @@
}
return WDT_NOT_WDT_REBOOT;
}
+#else
+int mtk_wdt_boot_check(void)
+{
+ unsigned int wdt_sta = wdt_reset_status;
+ int ret = WDT_NOT_WDT_REBOOT;
+
+ /*
+ * For DA download hope to timeout reboot, and boot to u-boot/kernel configurable reason,
+ * we set both timeout reboot and software reboot can check whether bypass power key.
+ */
+ if (wdt_sta & (MTK_WDT_STATUS_HWWDT_RST | MTK_WDT_STATUS_SWWDT_RST |
+ MTK_WDT_STATUS_MD_THERMAL_RST | MTK_WDT_STATUS_SPMWDT_RST |
+ MTK_WDT_STATUS_THERMAL_DIRECT_RST | MTK_WDT_STATUS_SECURITY_RST |
+ MTK_WDT_STATUS_DEBUGWDT_RST | MTK_WDT_STATUS_EINT_RST |
+ MTK_WDT_STATUS_SYSRST_RST | MTK_WDT_STATUS_DVFSP_RST | MTK_WDT_STATUS_MCUPM_RST |
+ MTK_WDT_STATUS_SSPM_RST)) {
+ if (rgu_mode & MTK_WDT_NONRST2_BYPASS_PWR_KEY) {
+ /* HW or SW reboot, and auto restart is set, means bypass power key */
+ ret = WDT_BY_PASS_PWK_REBOOT;
+
+ } else {
+
+ /* HW or SW reboot, but auto restart is not set, means NOT bypass power key */
+ ret = WDT_NORMAL_REBOOT;
+ }
+ } else {
+ /*
+ * For PMIC full reset, return "bypass pwr key reboot" to help AEE works.
+ * I.e., Prevent entering charger mode.
+ */
+ if (mtk_wdt_is_pmic_full_reset()) {
+ dprintf(INFO, "PMIC full rst: true\n");
+ ret = WDT_BY_PASS_PWK_REBOOT;
+ }
+ }
+
+ return ret;
+}
#endif
#else /* !ENABLE_WDT_MODULE */
diff --git a/src/bsp/lk/platform/mt2735/include/platform/mtk_wdt.h b/src/bsp/lk/platform/mt2735/include/platform/mtk_wdt.h
index f4b91b8..8d8a589 100644
--- a/src/bsp/lk/platform/mt2735/include/platform/mtk_wdt.h
+++ b/src/bsp/lk/platform/mt2735/include/platform/mtk_wdt.h
@@ -60,21 +60,28 @@
*/
void mtk_arch_reset(char bypass_pwrkey);
-#if LK_AS_BL33
-
#define WDT_NORMAL_REBOOT (0x100)
#define WDT_BY_PASS_PWK_REBOOT (0x200)
#define WDT_NOT_WDT_REBOOT (0x400)
/*WDT_STATUS*/
#define MTK_WDT_STATUS_HWWDT_RST_WITH_IRQ (0xA0000000)
-#define MTK_WDT_STATUS_HWWDT_RST (0x80000000)
-#define MTK_WDT_STATUS_SWWDT_RST (0x40000000)
-#define MTK_WDT_STATUS_IRQWDT_RST (0x20000000)
-#define MTK_WDT_STATUS_SECURITY_RST (1<<28)
-#define MTK_WDT_STATUS_DEBUGWDT_RST (0x00080000)
-#define MTK_WDT_STATUS_SPMWDT_RST (0x0001)
#define MTK_WDT_STATUS_THERMAL_CTL_RST (1<<18)
+#define MTK_WDT_STATUS_MD_THERMAL_RST (1)
+#define MTK_WDT_STATUS_SPMWDT_RST (1<<1)
+#define MTK_WDT_STATUS_EINT_RST (1<<2)
+#define MTK_WDT_STATUS_SYSRST_RST (1<<3)
+#define MTK_WDT_STATUS_DVFSP_RST (1<<4)
+#define MTK_WDT_STATUS_MCUPM_RST (1<<5)
+#define MTK_WDT_STATUS_PCIE_PERST_RST (1<<7)
+#define MTK_WDT_STATUS_SSPM_RST (1<<16)
+#define MTK_WDT_STATUS_MDDBG_RST (1<<17)
+#define MTK_WDT_STATUS_THERMAL_DIRECT_RST (1<<18)
+#define MTK_WDT_STATUS_DEBUGWDT_RST (1<<19)
+#define MTK_WDT_STATUS_SECURITY_RST (1<<28)
+#define MTK_WDT_STATUS_IRQWDT_RST (1<<29)
+#define MTK_WDT_STATUS_SWWDT_RST (1<<30)
+#define MTK_WDT_STATUS_HWWDT_RST (1<<31)
/* Reboot reason */
#define RE_BOOT_REASON_UNKNOW (0x00)
@@ -93,7 +100,4 @@
#define WDT_BY_PASS_PWK_REBOOT (0x200)
#define WDT_NOT_WDT_REBOOT (0x400)
-
int mtk_wdt_boot_check(void);
-
-#endif
diff --git a/src/bsp/lk/platform/mt2735/include/platform/platform_blx.h b/src/bsp/lk/platform/mt2735/include/platform/platform_blx.h
index ab0818d..929526f 100644
--- a/src/bsp/lk/platform/mt2735/include/platform/platform_blx.h
+++ b/src/bsp/lk/platform/mt2735/include/platform/platform_blx.h
@@ -36,6 +36,9 @@
BR_KERNEL_PANIC,
BR_WDT_SW,
BR_WST_HW,
+ BR_POWER_EXC = 30,
+ BR_LONG_POWKEY,
+ BR_POWER_LOSS
} boot_reason_t;
void platform_memory_init(void);
diff --git a/src/bsp/lk/platform/mt2735/platform_bl2.c b/src/bsp/lk/platform/mt2735/platform_bl2.c
index c18da3f..630a31d 100644
--- a/src/bsp/lk/platform/mt2735/platform_bl2.c
+++ b/src/bsp/lk/platform/mt2735/platform_bl2.c
@@ -50,6 +50,7 @@
#include <platform/rtc.h>
#include <boot_args.h>
#include <string.h>
+#include <platform/platform_blx.h>
static uint32_t g_boot_reason = 0;
static uint32_t g_rgu_mode = 0;
@@ -118,6 +119,22 @@
};
#endif /* WITH_KERNEL_VM */
+extern U32 upmu_is_chr_det(void);
+
+int usb_accessory_in(void)
+{
+#if !CFG_FPGA_PLATFORM
+ int exist = 0;
+
+ if (upmu_is_chr_det() == KAL_TRUE) {
+ exist = 1;
+ }
+ return exist;
+#else
+ return 1;
+#endif
+}
+
static inline size_t query_plat_dram_sz(void)
{
return mt_mem_size();
@@ -237,6 +254,8 @@
void platform_early_init_blx(void)
{
+ boot_reason_t reason;
+
mtk_timer_init();
#if !CFG_FPGA_PLATFORM
@@ -274,6 +293,12 @@
clk_buf_init();
mt_dcm_init();
+
+
+ reason = platform_boot_status();
+ if (reason == BR_RTC || reason == BR_POWER_KEY || reason == BR_USB || reason == BR_WDT || reason == BR_WDT_BY_PASS_PWK || reason == BR_2SEC_REBOOT)
+ rtc_bbpu_power_on();
+
#endif
bgr_init();
@@ -281,6 +306,65 @@
dprintf(CRITICAL, "BL2 Build Time: %s %s\n", __DATE__, __TIME__);
}
+int platform_wdt_boot_check(void)
+{
+ return mtk_wdt_boot_check();
+}
+
+boot_reason_t platform_boot_status(void)
+{
+ u32 pmic_reboot = get_pmic_boot_status();
+
+ if (rtc_boot_check()) {
+ dprintf(CRITICAL, "%s RTC boot!\n");
+ return BR_RTC;
+ }
+
+ if (pmic_reboot == 1) {
+ dprintf(CRITICAL, "pmic: power exception(OC/PG)!\n");
+ return BR_POWER_EXC;
+ } else if (pmic_reboot == 2) {
+ dprintf(CRITICAL, "pmic: long power key press reboot!\n");
+ return BR_LONG_POWKEY;
+ } else if (pmic_reboot == 3) {
+ if(rtc_2sec_reboot_check()) {
+ dprintf(CRITICAL, "pmic: 2sec reboot!\n");
+ return BR_2SEC_REBOOT;
+ } else {
+ dprintf(CRITICAL, "pmic: power miss!\n");
+ return BR_POWER_LOSS;
+ }
+ }
+
+ if (platform_wdt_boot_check() == WDT_NORMAL_REBOOT) {
+ dprintf(CRITICAL, "SW reset without bypass power key flag\n");
+ dprintf(CRITICAL, "WDT normal boot!\n");
+ return BR_WDT;
+ } else if(platform_wdt_boot_check() == WDT_BY_PASS_PWK_REBOOT) {
+ dprintf(CRITICAL, "SW reset with bypass power key flag\n");
+ dprintf(CRITICAL, "WDT reboot bypass power key!\n");
+ return BR_WDT_BY_PASS_PWK;
+ }
+
+ if (usb_accessory_in()) {
+ dprintf(CRITICAL, "%s USB/charger boot!\n");
+ return BR_USB;
+ }
+
+#if !CFG_FPGA_PLATFORM
+ /* check power key */
+ if ((pmic_detect_powerkey() && !is_pwrkey_short_press())
+ || is_pmic_long_press_reset()) {
+ dprintf(CRITICAL, "%s Power key boot!\n");
+ return BR_POWER_KEY;
+ }
+#endif
+
+ pl_power_off();
+
+ return BR_UNKNOWN;
+}
+
void platform_init_blx()
{
}
diff --git a/src/kernel/linux/v4.19/drivers/rtc/interface.c b/src/kernel/linux/v4.19/drivers/rtc/interface.c
index ce051f9..089ff8c 100644
--- a/src/kernel/linux/v4.19/drivers/rtc/interface.c
+++ b/src/kernel/linux/v4.19/drivers/rtc/interface.c
@@ -482,6 +482,7 @@
rtc->aie_timer.node.expires = rtc_tm_to_ktime(alarm->time);
rtc->aie_timer.period = 0;
+ rtc->aie_timer.enabled = alarm->enabled;
if (alarm->enabled)
err = rtc_timer_enqueue(rtc, &rtc->aie_timer);
@@ -821,7 +822,8 @@
struct rtc_time tm;
ktime_t now;
- timer->enabled = 1;
+ if(timer->enabled == 0)
+ timer->enabled = 1;
__rtc_read_time(rtc, &tm);
now = rtc_tm_to_ktime(tm);
@@ -838,7 +840,7 @@
struct rtc_wkalrm alarm;
int err;
alarm.time = rtc_ktime_to_tm(timer->node.expires);
- alarm.enabled = 1;
+ alarm.enabled = timer->enabled;
err = __rtc_set_alarm(rtc, &alarm);
if (err == -ETIME) {
pm_stay_awake(rtc->dev.parent);
diff --git a/src/kernel/linux/v4.19/drivers/rtc/rtc-mt6330.c b/src/kernel/linux/v4.19/drivers/rtc/rtc-mt6330.c
index 3dc075c..1b373a8 100644
--- a/src/kernel/linux/v4.19/drivers/rtc/rtc-mt6330.c
+++ b/src/kernel/linux/v4.19/drivers/rtc/rtc-mt6330.c
@@ -79,9 +79,16 @@
#define RTC_AL_MTH_MASK 0x000f
#define RTC_AL_YEA_MASK 0x007f
+#define RTC_PDN1 0x002c
+#define RTC_PDN1_PWRON_TIME BIT(7)
+
#define RTC_PDN2 0x002e
#define RTC_PDN2_PWRON_ALARM BIT(4)
+#define RTC_SPAR0 0x0030
+
+#define RTC_SPAR1 0x0032
+
#define RTC_SPAR0_L 0x0030
#define RTC_INT_CNT_L 0x0040
@@ -90,8 +97,34 @@
#define RTC_NUM_YEARS 128
#define RTC_MIN_YEAR_OFFSET (RTC_MIN_YEAR - RTC_BASE_YEAR)
+#define RTC_PWRON_YEA RTC_PDN2
+#define RTC_PWRON_YEA_MASK 0x7f00
+#define RTC_PWRON_YEA_SHIFT 8
+
+#define RTC_PWRON_MTH RTC_PDN2
+#define RTC_PWRON_MTH_MASK 0x000f
+#define RTC_PWRON_MTH_SHIFT 0
+
+#define RTC_PWRON_SEC RTC_SPAR0
+#define RTC_PWRON_SEC_MASK 0x003f
+#define RTC_PWRON_SEC_SHIFT 0
+
+#define RTC_PWRON_MIN RTC_SPAR1
+#define RTC_PWRON_MIN_MASK 0x003f
+#define RTC_PWRON_MIN_SHIFT 0
+
+#define RTC_PWRON_HOU RTC_SPAR1
+#define RTC_PWRON_HOU_MASK 0x07c0
+#define RTC_PWRON_HOU_SHIFT 6
+
+#define RTC_PWRON_DOM RTC_SPAR1
+#define RTC_PWRON_DOM_MASK 0xf800
+#define RTC_PWRON_DOM_SHIFT 11
+
#define SPARE_REG_WIDTH 1
+#define RTC_POFF_ALM_SET _IOW('p', 0x15, struct rtc_time) /* Set alarm time */
+
enum mtk_rtc_spare_enum {
SPARE_AL_HOU,
SPARE_AL_MTH,
@@ -99,6 +132,12 @@
SPARE_RG_MAX,
};
+enum rtc_reg_set {
+ RTC_REG,
+ RTC_MASK,
+ RTC_SHIFT
+};
+
enum rtc_eosc_cali_td {
EOSC_CALI_TD_01_SEC = 0x3,
EOSC_CALI_TD_02_SEC,
@@ -141,6 +180,16 @@
static int mtk_rtc_write_trigger(struct mt6330_rtc *rtc);
+static u16 rtc_pwron_reg[RTC_OFFSET_COUNT][3] = {
+ {RTC_PWRON_SEC, RTC_PWRON_SEC_MASK, RTC_PWRON_SEC_SHIFT},
+ {RTC_PWRON_MIN, RTC_PWRON_MIN_MASK, RTC_PWRON_MIN_SHIFT},
+ {RTC_PWRON_HOU, RTC_PWRON_HOU_MASK, RTC_PWRON_HOU_SHIFT},
+ {RTC_PWRON_DOM, RTC_PWRON_DOM_MASK, RTC_PWRON_DOM_SHIFT},
+ {0, 0, 0},
+ {RTC_PWRON_MTH, RTC_PWRON_MTH_MASK, RTC_PWRON_MTH_SHIFT},
+ {RTC_PWRON_YEA, RTC_PWRON_YEA_MASK, RTC_PWRON_YEA_SHIFT},
+};
+
static const struct reg_field mt6330_cali_reg_fields[CALI_FILED_MAX] = {
[RTC_EOSC32_CK_PDN] = REG_FIELD(MT6330_SCK_TOP_CKPDN_CON0_L, 2, 2),
[EOSC_CALI_TD] = REG_FIELD(MT6330_RTC_AL_DOW_L, 8, 10),
@@ -248,7 +297,7 @@
}
// else, not access RTC register
- ret = regmap_bulk_write(rtc->regmap, reg, val, val_count);
+ ret = regmap_bulk_write(rtc->regmap, reg, val, val_count);
if (reg == rtc->addr_base + rtc->dev_comp->wrtgr_addr) {
// need 1 ms delay to make sure write completely
@@ -442,6 +491,81 @@
return ret;
}
+static void mtk_rtc_set_pwron_time(struct mt6330_rtc *rtc, struct rtc_time *tm)
+{
+ u32 data[RTC_OFFSET_COUNT];
+ int ret, i;
+
+ data[RTC_OFFSET_SEC] =
+ ((tm->tm_sec << RTC_PWRON_SEC_SHIFT) & RTC_PWRON_SEC_MASK);
+ data[RTC_OFFSET_MIN] =
+ ((tm->tm_min << RTC_PWRON_MIN_SHIFT) & RTC_PWRON_MIN_MASK);
+ data[RTC_OFFSET_HOUR] =
+ ((tm->tm_hour << RTC_PWRON_HOU_SHIFT) & RTC_PWRON_HOU_MASK);
+ data[RTC_OFFSET_DOM] =
+ ((tm->tm_mday << RTC_PWRON_DOM_SHIFT) & RTC_PWRON_DOM_MASK);
+ data[RTC_OFFSET_MTH] =
+ ((tm->tm_mon << RTC_PWRON_MTH_SHIFT) & RTC_PWRON_MTH_MASK);
+ data[RTC_OFFSET_YEAR] =
+ ((tm->tm_year << RTC_PWRON_YEA_SHIFT) & RTC_PWRON_YEA_MASK);
+
+ printk_deferred(" %s set PWRON_SEC %x\n", __func__, data[RTC_OFFSET_SEC]);
+
+ for (i = RTC_OFFSET_SEC; i < RTC_OFFSET_COUNT; i++) {
+ if (i == RTC_OFFSET_DOW)
+ continue;
+ ret = rtc_update_bits(rtc,
+ rtc->addr_base + rtc_pwron_reg[i][RTC_REG],
+ rtc_pwron_reg[i][RTC_MASK],
+ data[i]);
+
+ if (ret < 0)
+ goto exit;
+ mtk_rtc_write_trigger(rtc);
+ }
+
+ if (ret < 0)
+ goto exit;
+
+ return;
+
+exit:
+ dev_err(rtc->dev, "%s error\n", __func__);
+ printk_deferred("%s error\n", __func__);
+
+}
+
+void mtk_rtc_save_pwron_time(struct mt6330_rtc *rtc,
+ bool enable, struct rtc_time *tm)
+{
+ u32 pdn1 = 0;
+ int ret;
+
+ /* set power on time */
+ mtk_rtc_set_pwron_time(rtc, tm);
+
+ /* update power on alarm related flags */
+ if (enable)
+ pdn1 = RTC_PDN1_PWRON_TIME;
+
+ ret = rtc_update_bits(rtc,
+ rtc->addr_base + RTC_PDN1,
+ RTC_PDN1_PWRON_TIME,
+ pdn1);
+
+ mtk_rtc_write_trigger(rtc);
+
+ if (ret < 0)
+ goto exit;
+
+// mtk_rtc_write_trigger(rtc);
+
+ return;
+
+exit:
+ dev_err(rtc->dev, "%s error\n", __func__);
+}
+
static int mtk_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
time64_t time;
@@ -557,6 +681,16 @@
struct mt6330_rtc *rtc = dev_get_drvdata(dev);
int ret;
u16 data[RTC_OFFSET_COUNT] = { 0 };
+ ktime_t target;
+
+ printk_deferred(" %s set %d\n", __func__, alm->enabled);
+
+ if (alm->enabled == 1) {
+ /* Add one more second to postpone wake time. */
+ target = rtc_tm_to_ktime(*tm);
+ target = ktime_add_ns(target, NSEC_PER_SEC);
+ *tm = rtc_ktime_to_tm(target);
+ }
if (tm->tm_year > 195) {
dev_err(rtc->dev, "%s: invalid year %04d > 2095\n",
@@ -568,6 +702,29 @@
tm->tm_mon++;
mutex_lock(&rtc->lock);
+
+ switch (alm->enabled) {
+ case 3:
+ /* enable power-on alarm with logo */
+ mtk_rtc_save_pwron_time(rtc, true, tm);
+ break;
+ case 4:
+ /* disable power-on alarm */
+ mtk_rtc_save_pwron_time(rtc, false, tm);
+ break;
+ default:
+ break;
+ }
+
+ ret = rtc_update_bits(rtc,
+ rtc->addr_base + RTC_PDN2,
+ RTC_PDN2_PWRON_ALARM,
+ 0);
+
+ if (ret < 0)
+ goto exit;
+ mtk_rtc_write_trigger(rtc);
+
ret = rtc_bulk_read(rtc, rtc->addr_base + RTC_AL_SEC,
data, RTC_OFFSET_COUNT * 2);
if (ret < 0)
@@ -586,6 +743,8 @@
data[RTC_OFFSET_YEAR] = ((data[RTC_OFFSET_YEAR] & ~(RTC_AL_YEA_MASK)) |
(tm->tm_year & RTC_AL_YEA_MASK));
+ printk_deferred(" %s set AL_SEC %x\n", __func__, data[RTC_OFFSET_SEC]);
+
if (alm->enabled) {
ret = rtc_bulk_read(rtc, rtc->addr_base + RTC_AL_SEC,
data, RTC_OFFSET_COUNT * 2);
@@ -642,7 +801,54 @@
return ret;
}
+int alarm_set_power_on(struct device *dev, struct rtc_wkalrm *alm)
+{
+ int err = 0;
+ struct rtc_time tm;
+ time64_t now, scheduled;
+
+ err = rtc_valid_tm(&alm->time);
+ if (err != 0)
+ return err;
+ scheduled = rtc_tm_to_time64(&alm->time);
+
+ err = mtk_rtc_read_time(dev, &tm);
+ if (err != 0)
+ return err;
+ now = rtc_tm_to_time64(&tm);
+
+ if (scheduled <= now)
+ alm->enabled = 4;
+ else
+ alm->enabled = 3;
+
+ mtk_rtc_set_alarm(dev, alm);
+
+ return err;
+}
+
+static int mtk_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+ void __user *uarg = (void __user *) arg;
+ int err = 0;
+ struct rtc_wkalrm alm;
+
+ switch (cmd) {
+ case RTC_POFF_ALM_SET:
+ if (copy_from_user(&alm.time, uarg, sizeof(alm.time)))
+ return -EFAULT;
+ err = alarm_set_power_on(dev, &alm);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+
static const struct rtc_class_ops mtk_rtc_ops = {
+ .ioctl = mtk_rtc_ioctl,
.read_time = mtk_rtc_read_time,
.set_time = mtk_rtc_set_time,
.read_alarm = mtk_rtc_read_alarm,
diff --git a/src/kernel/linux/v4.19/drivers/rtc/rtc-sysfs.c b/src/kernel/linux/v4.19/drivers/rtc/rtc-sysfs.c
index 9746c32..a1dc393 100644
--- a/src/kernel/linux/v4.19/drivers/rtc/rtc-sysfs.c
+++ b/src/kernel/linux/v4.19/drivers/rtc/rtc-sysfs.c
@@ -165,6 +165,7 @@
const char *buf_ptr;
int adjust = 0;
+ printk_deferred("%s \n", __func__);
/* Only request alarms that trigger in the future. Disable them
* by writing another time, e.g. 0 meaning Jan 1 1970 UTC.
*/
@@ -221,6 +222,101 @@
static DEVICE_ATTR_RW(wakealarm);
static ssize_t
+poweralarm_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ ssize_t retval;
+ time64_t alarm;
+ struct rtc_wkalrm alm;
+
+ /* Don't show disabled alarms. For uniformity, RTC alarms are
+ * conceptually one-shot, even though some common RTCs (on PCs)
+ * don't actually work that way.
+ *
+ * NOTE: RTC implementations where the alarm doesn't match an
+ * exact YYYY-MM-DD HH:MM[:SS] date *must* disable their RTC
+ * alarms after they trigger, to ensure one-shot semantics.
+ */
+ retval = rtc_read_alarm(to_rtc_device(dev), &alm);
+ if (retval == 0 && alm.enabled) {
+ alarm = rtc_tm_to_time64(&alm.time);
+ retval = sprintf(buf, "%lld\n", alarm);
+ }
+
+ return retval;
+}
+
+
+static ssize_t
+poweralarm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ ssize_t retval;
+ time64_t now, alarm;
+ time64_t push = 0;
+ struct rtc_wkalrm alm;
+ struct rtc_device *rtc = to_rtc_device(dev);
+ const char *buf_ptr;
+ int adjust = 0;
+
+ printk_deferred("%s \n", __func__);
+
+ /* Only request alarms that trigger in the future. Disable them
+ * by writing another time, e.g. 0 meaning Jan 1 1970 UTC.
+ */
+ retval = rtc_read_time(rtc, &alm.time);
+ if (retval < 0)
+ return retval;
+ now = rtc_tm_to_time64(&alm.time);
+
+ buf_ptr = buf;
+ if (*buf_ptr == '+') {
+ buf_ptr++;
+ if (*buf_ptr == '=') {
+ buf_ptr++;
+ push = 1;
+ } else
+ adjust = 1;
+ }
+ retval = kstrtos64(buf_ptr, 0, &alarm);
+ if (retval)
+ return retval;
+ if (adjust) {
+ alarm += now;
+ }
+ if (alarm > now || push) {
+ /* Avoid accidentally clobbering active alarms; we can't
+ * entirely prevent that here, without even the minimal
+ * locking from the /dev/rtcN api.
+ */
+ retval = rtc_read_alarm(rtc, &alm);
+ if (retval < 0)
+ return retval;
+ if (alm.enabled) {
+
+ if (push) {
+ push = rtc_tm_to_time64(&alm.time);
+ alarm += push;
+ } else
+ return -EBUSY;
+ } else if (push)
+ return -EINVAL;
+ alm.enabled = 3;
+ } else {
+ alm.enabled = 4;
+
+ /* Provide a valid future alarm time. Linux isn't EFI,
+ * this time won't be ignored when disabling the alarm.
+ */
+ alarm = now + 300;
+ }
+ rtc_time64_to_tm(alarm, &alm.time);
+
+ retval = rtc_set_alarm(rtc, &alm);
+ return (retval < 0) ? retval : n;
+}
+static DEVICE_ATTR_RW(poweralarm);
+
+static ssize_t
offset_show(struct device *dev, struct device_attribute *attr, char *buf)
{
ssize_t retval;
@@ -264,6 +360,7 @@
&dev_attr_max_user_freq.attr,
&dev_attr_hctosys.attr,
&dev_attr_wakealarm.attr,
+ &dev_attr_poweralarm.attr,
&dev_attr_offset.attr,
&dev_attr_range.attr,
NULL,
diff --git a/src/lynq/lib/libpoweralarm/LICENSE b/src/lynq/lib/libpoweralarm/LICENSE
new file mode 100644
index 0000000..77f59ed
--- /dev/null
+++ b/src/lynq/lib/libpoweralarm/LICENSE
@@ -0,0 +1,31 @@
+Copyright Statement:
+
+This software/firmware and related documentation ("MediaTek Software") are
+protected under relevant copyright laws. The information contained herein is
+confidential and proprietary to MediaTek Inc. and/or its licensors. Without
+the prior written permission of MediaTek inc. and/or its licensors, any
+reproduction, modification, use or disclosure of MediaTek Software, and
+information contained herein, in whole or in part, shall be strictly
+prohibited.
+
+MediaTek Inc. (C) 2015. All rights reserved.
+
+BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
+ON AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL
+WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
+NONINFRINGEMENT. NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH
+RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
+INCORPORATED IN, OR SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES
+TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
+RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
+OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN MEDIATEK
+SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK SOFTWARE
+RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S
+ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE
+RELEASED HEREUNDER WILL BE, AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE
+MEDIATEK SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
+CHARGE PAID BY RECEIVER TO MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
diff --git a/src/lynq/lib/libpoweralarm/format_change.c b/src/lynq/lib/libpoweralarm/format_change.c
new file mode 100644
index 0000000..d9cac51
--- /dev/null
+++ b/src/lynq/lib/libpoweralarm/format_change.c
@@ -0,0 +1,133 @@
+#include"./include/format_change.h"
+#include<log/log.h>
+
+
+/*****************************************************************************
+* Prototype : format_change
+* Description : convert the incoming fixed format string into the corresponding alarm seconds
+* Input : char *buffer ; input format : 2022-04-23-15-30-00 ( Mon-Day-Hour-Min-Sec ) Or 1200 ( seconds )
+* Output : None
+* Return Value : -1: match error ; >0: set to wake up the devices after seconds
+*
+*****************************************************************************/
+ssize_t format_change(char *buffer)
+{
+ time_t rawtime;
+ time_t alarm_tamp;
+ struct tm *info = NULL;
+ struct tm *set_alarm = NULL;
+ char *time_buff = NULL;
+
+ char ebuff[256];
+ regex_t reg_alm,reg_sec;
+ char *pattern_setalm = "^20[0-9][0-9]-[01]?[1-9]-[0-3]?[0-9]-[0-2]?[0-9]-[0-5]?[0-9]-[0-5]?[0-9]$";//regular expression 1 , in order that match alarm time string eg:Mon-Day-Hour-Min-Sec
+ char *pattern_setsec = "^[1-9][0-9]*$";//regular expression 2 , in order that match seconds
+ int cflags = REG_EXTENDED | REG_ICASE | REG_NOSUB;//POSIX extend,not care up and low letters,not store result
+
+ int ret;
+ ssize_t sec;
+
+ ret = regcomp(®_alm,pattern_setalm,cflags); //匹配规则1:\d\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d
+ if(ret) // judge error code : 0 symbolize success ; other value symbolizes fail
+ {
+ regerror(ret,®_alm,ebuff,256);
+ ALOGI(ebuff);
+ regfree(®_alm); //free mattch pattern
+ regfree(®_sec);
+ return -1;
+ }
+
+ ret = regcomp(®_sec,pattern_setsec,cflags); //匹配规则2:^\d\d*$
+ if(ret)// judge error code : 0 symbolize success ; other value symbolizes fail
+ {
+ regerror(ret,®_sec,ebuff,256);
+ ALOGI(ebuff);
+ regfree(®_alm); //free mattch pattern
+ regfree(®_sec);
+ return -1;
+ }
+
+ time(&rawtime); //获取自1970年到现在一共过去了几秒,赋值给rawtime
+
+ info =localtime(&rawtime); //转换为UTC时间
+
+ bool leap_flag = (bool)(((info->tm_year%4==0)&&(info->tm_year%100!=0)) || (info->tm_year%400 == 0)); //判断当前是否为闰年
+ int day_array[13] = {0,31, leap_flag?29:28,31,30,31,30,31,31,30,31,30,31}; //设置每个月的天数
+
+ if((ret = regexec(®_alm,buffer,0,NULL,0)) == 0) //Retrieve the incoming buffer string according to matching rule 1
+ {
+ set_alarm = (struct tm *)malloc(sizeof(struct tm));
+ memset(set_alarm,0,sizeof(struct tm));
+ memcpy(set_alarm,info,sizeof(struct tm)); //拷贝当前时间信息
+
+ ret = sscanf(buffer,"%d-%d-%d-%d-%d-%d",&(set_alarm->tm_year),&(set_alarm->tm_mon),&(set_alarm->tm_mday),&(set_alarm->tm_hour),&(set_alarm->tm_min),&(set_alarm->tm_sec));//read data form formatted string
+
+ if(ret == -1) //sscanf no mattch
+ {
+ ALOGI("sscanf error code -1\n");
+ free(set_alarm);
+ return -1;
+ }
+ else if(ret == 6) //Success mattch
+ {
+ if((set_alarm->tm_hour > 23) || (set_alarm->tm_hour < 0)) //judge hour
+ {
+ ALOGI("hour error\n");
+ ret = -1;
+ }
+ if((set_alarm->tm_mon > 12) || (set_alarm->tm_mon < 1)) //judge month
+ {
+ ALOGI("mon error\n");
+ ret = -1;
+ }
+ if((set_alarm->tm_mday > day_array[set_alarm->tm_mon]) || (set_alarm->tm_mday < 1)) //judge day
+ {
+ ALOGI("day error\n");
+ ret = -1;
+ }
+ if(ret == -1) //Error setting alarm time
+ {
+ free(set_alarm);
+ return -1;
+ }
+
+ set_alarm->tm_mon -= 1;
+ sprintf(ebuff,"set alarm is %s\n",asctime(set_alarm)); //print log
+ ALOGI(ebuff);
+ alarm_tamp = mktime(set_alarm); //struct tm transform struct time_t
+ time(&rawtime);
+ //printf("tamp: %ld\n",alarm_tamp);
+ sec = alarm_tamp - rawtime; //set second
+ free(set_alarm);
+
+ if(sec <= 0) //the current alarm time is less than the current system time
+ {
+ ALOGI("sec <= 0 setalarm error\n");
+ return -1;
+ }
+ }
+ else //sscanf mattch fail
+ {
+ ALOGI("sscanf error other\n");
+ free(set_alarm);
+ return -1;
+ }
+ }
+ else if ((ret = regexec(®_sec,buffer,0,NULL,0)) == 0) //Retrieve the incoming buffer string according to matching rule 2
+ {
+ sec = (ssize_t)atoi(buffer); //string convert ssize_t
+ }
+ else //matching rule 1 and 2 all fail
+ {
+ regerror(ret,®_sec,ebuff,256); //free memony
+ ALOGI(ebuff);
+ regfree(®_alm);
+ regfree(®_sec);
+ return -1;
+ }
+
+ regfree(®_alm);
+ regfree(®_sec);
+
+ return sec;
+}
\ No newline at end of file
diff --git a/src/lynq/lib/libpoweralarm/include/format_change.h b/src/lynq/lib/libpoweralarm/include/format_change.h
new file mode 100644
index 0000000..0656c9f
--- /dev/null
+++ b/src/lynq/lib/libpoweralarm/include/format_change.h
@@ -0,0 +1,15 @@
+#ifndef _FORMAT_CHANGE_H_
+#define _FORMAT_CHANGE_H_
+
+#include<stdio.h>
+#include<stdlib.h>
+#include<regex.h>
+#include<stdbool.h>
+#include<time.h>
+#include<string.h>
+
+
+ssize_t format_change(char *buffer);
+
+
+#endif
\ No newline at end of file
diff --git a/src/lynq/lib/libpoweralarm/libpoweralarm.c b/src/lynq/lib/libpoweralarm/libpoweralarm.c
new file mode 100644
index 0000000..ac3203c
--- /dev/null
+++ b/src/lynq/lib/libpoweralarm/libpoweralarm.c
@@ -0,0 +1,86 @@
+#include<stdio.h>
+#include<stdlib.h>
+#include<unistd.h>
+#include<stdbool.h>
+#include<log/log.h>
+#include"./include/format_change.h"
+
+
+#define LOG_TAG "libpoweralarm"
+#define RTCFILE_POWERALARM "/sys/class/rtc/rtc0/poweralarm"
+
+#define RTCFILE_WAKEALARM "/sys/class/rtc/rtc0/wakealarm"
+
+extern "C" ssize_t wakealarm(char *buffer);
+
+
+extern "C" ssize_t poweralarm(char *buffer);
+
+ssize_t poweralarm(char *buffer);
+
+ssize_t wakealarm(char *buffer);
+/*****************************************************************************
+*
+* Prototype : poweralarm
+* Description : set shutdown wake-up alarm clock
+* Input : char *buffer ; input format : 04-23-15-30-00 ( Mon-Day-Hour-Min-Sec ) Or 1200 ( seconds )
+* Output : None
+* Return Value : -1: error ; >0: set to wake up the devices after seconds
+*
+*****************************************************************************/
+ssize_t poweralarm(char *buffer)
+{
+ ssize_t sec;
+ char *time_buff = NULL;
+
+ sec = format_change(buffer); //computing seconds for shutdown alarm
+ if(sec < 0)
+ {
+ ALOGI("No Mattch\n");
+ return -1;
+ }
+
+ time_buff = (char*)malloc(100);
+ bzero(time_buff,100);
+
+ sprintf(time_buff,"echo +%ld > %s",sec,RTCFILE_POWERALARM); //write formatted data into time_buff
+ system(time_buff);
+ ALOGI(time_buff);
+
+ free(time_buff);
+
+ return sec; // wake-up devices after sec seconds
+}
+
+
+/*****************************************************************************
+* Prototype : wakealarm
+* Description : set the wake-up alarm clock in low power mode
+* Input : char *buffer ; input format : 04-23-15-30-00 ( Mon-Day-Hour-Min-Sec ) Or 1200 ( seconds )
+* Output : None
+* Return Value : -1: error ; >0: set to wake up the devices after seconds
+*
+*****************************************************************************/
+ssize_t wakealarm(char *buffer)
+{
+ ssize_t sec;
+ char *time_buff = NULL;
+
+ sec = format_change(buffer); //computing seconds for lowpower alarm
+ if(sec < 0)
+ {
+ ALOGI("No Mattch\n");
+ return -1;
+ }
+
+ time_buff = (char*)malloc(100);
+ bzero(time_buff,100);
+
+ sprintf(time_buff,"echo +%ld > %s",sec,RTCFILE_WAKEALARM); //write formatted data into time_buff
+ system(time_buff);
+ ALOGI(time_buff);
+
+ free(time_buff);
+
+ return sec; // wake-up devices after sec seconds
+}
diff --git a/src/lynq/lib/libpoweralarm/makefile b/src/lynq/lib/libpoweralarm/makefile
new file mode 100644
index 0000000..3cdf04f
--- /dev/null
+++ b/src/lynq/lib/libpoweralarm/makefile
@@ -0,0 +1,69 @@
+SHELL = /bin/sh
+RM = rm -f
+
+LOCAL_CFLAGS := -Wall \
+ -std=gnu++14 \
+ -g -Os \
+ -flto \
+ -DRIL_SHLIB \
+ -DATCI_PARSE \
+ -fPIC \
+ -DKEEP_ALIVE \
+ -DECALL_SUPPORT \
+
+
+
+
+$(warning ################# libautosuspend ROOT: $(ROOT),includedir:$(includedir))
+LOCAL_PATH = .
+
+LOCAL_C_INCLUDES = \
+ -I. \
+ -I$(ROOT)$(includedir)/logger \
+ -I$(ROOT)$(includedir)/liblog \
+
+
+LOCAL_LIBS := \
+ -L. \
+ -ldl \
+ -lstdc++ \
+ -llog \
+ -lcutils \
+ -lutils \
+ -lbinder \
+ -lpthread \
+ -llynq-log \
+
+
+SOURCES = $(wildcard *.c)
+
+EXECUTABLE = libpoweralarm.so
+
+OBJECTS=$(SOURCES:.c=.o)
+
+
+.PHONY: build clean install pack_rootfs
+all: build
+$(EXECUTABLE): $(OBJECTS)
+ $(CXX) -shared -Wl,--no-undefined $(OBJECTS) $(LOCAL_LIBS) $(LOCAL_CFLAGS) $(LOCAL_C_INCLUDES) -o $@
+
+%.o : %.c
+ $(CXX) $(LOCAL_C_INCLUDES) $(LOCAL_CFLAGS) $(LOCAL_LIBS) -o $@ -c $<
+
+build: $(EXECUTABLE)
+ $(warning ########## build $(EXECUTABLE) ##########)
+install:
+ mkdir -p $(ROOT)$(base_libdir)/
+ install $(EXECUTABLE) $(ROOT)$(base_libdir)/
+ mkdir -p $(ROOT)$(includedir)/$(NAME)/sdk
+pack_rootfs:
+ mkdir -p $(PACK_INITRAMFS_TO)$(base_libdir)/
+ cp -af $(EXECUTABLE) $(PACK_INITRAMFS_TO)$(base_libdir)/
+ $(CROSS)strip $(PACK_INITRAMFS_TO)$(base_libdir)/$(EXECUTABLE)
+ mkdir -p $(PACK_TO)$(base_libdir)/
+ cp -af $(EXECUTABLE) $(PACK_TO)$(base_libdir)/
+ $(CROSS)strip $(PACK_TO)$(base_libdir)/$(EXECUTABLE)
+.PHONY: clean
+clean:
+ $(RM) $(OBJECTS) $(EXECUTABLE)
+ -find . -name "*.o" -delete