/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2016 MediaTek Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* For KPOC alarm */ #include #include #include #include #include #include #include #include "../misc/mediatek/include/mt-plat/mtk_boot_common.h" #include "../misc/mediatek/include/mt-plat/mtk_reboot.h" #include "../misc/mediatek/include/mt-plat/mtk_rtc.h" #ifdef pr_fmt #undef pr_fmt #endif #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define RTC_NAME "mt-rtc" #define IPIMB /* we map HW YEA 0 (2000) to 1968 not 1970 because 2000 is the leap year */ #define RTC_MIN_YEAR 1968 #define RTC_BASE_YEAR 1900 #define RTC_MIN_YEAR_OFFSET (RTC_MIN_YEAR - RTC_BASE_YEAR) /* Min, Hour, Dom... register offset to RTC_TC_SEC */ #define RTC_OFFSET_SEC 0 #define RTC_OFFSET_MIN 1 #define RTC_OFFSET_HOUR 2 #define RTC_OFFSET_DOM 3 #define RTC_OFFSET_DOW 4 #define RTC_OFFSET_MTH 5 #define RTC_OFFSET_YEAR 6 #define RTC_OFFSET_COUNT 7 #define RTC_DSN_ID 0x580 #define RTC_BBPU 0x8 #define RTC_IRQ_STA 0xa #define RTC_IRQ_EN 0xc #define RTC_AL_MASK 0x10 #define RTC_TC_SEC 0x12 #define RTC_AL_SEC 0x20 #define RTC_AL_MIN 0x22 #define RTC_AL_HOU 0x24 #define RTC_AL_DOM 0x26 #define RTC_AL_DOW 0x28 #define RTC_AL_MTH 0x2a #define RTC_AL_YEA 0x2c #define RTC_OSC32CON 0x2e #define RTC_POWERKEY1 0x30 #define RTC_POWERKEY2 0x32 #define RTC_PDN1 0x34 #define RTC_PDN2 0x36 #define RTC_SPAR0 0x38 #define RTC_SPAR1 0x3a #define RTC_PROT 0x3c #define RTC_WRTGR 0x42 #define RTC_CON 0x44 #define RTC_TC_SEC_MASK 0x3f #define RTC_TC_MIN_MASK 0x3f #define RTC_TC_HOU_MASK 0x1f #define RTC_TC_DOM_MASK 0x1f #define RTC_TC_DOW_MASK 0x7 #define RTC_TC_MTH_MASK 0xf #define RTC_TC_YEA_MASK 0x7f #define RTC_AL_SEC_MASK 0x3f #define RTC_AL_MIN_MASK 0x3f #define RTC_AL_HOU_MASK 0x1f #define RTC_AL_DOM_MASK 0x1f #define RTC_AL_DOW_MASK 0x7 #define RTC_AL_MTH_MASK 0xf #define RTC_AL_YEA_MASK 0x7f #define RTC_PWRON_SEC_SHIFT 0x0 #define RTC_PWRON_MIN_SHIFT 0x0 #define RTC_PWRON_HOU_SHIFT 0x6 #define RTC_PWRON_DOM_SHIFT 0xb #define RTC_PWRON_MTH_SHIFT 0x0 #define RTC_PWRON_YEA_SHIFT 0x8 #define RTC_PWRON_SEC_MASK (RTC_AL_SEC_MASK << RTC_PWRON_SEC_SHIFT) #define RTC_PWRON_MIN_MASK (RTC_AL_MIN_MASK << RTC_PWRON_MIN_SHIFT) #define RTC_PWRON_HOU_MASK (RTC_AL_HOU_MASK << RTC_PWRON_HOU_SHIFT) #define RTC_PWRON_DOM_MASK (RTC_AL_DOM_MASK << RTC_PWRON_DOM_SHIFT) #define RTC_PWRON_MTH_MASK (RTC_AL_MTH_MASK << RTC_PWRON_MTH_SHIFT) #define RTC_PWRON_YEA_MASK (RTC_AL_YEA_MASK << RTC_PWRON_YEA_SHIFT) /* Common */ #define RTC_BBPU_KEY 0x4300 #define RTC_BBPU_CBUSY BIT(6) #define RTC_BBPU_RELOAD BIT(5) #define RTC_BBPU_PWREN BIT(0) /* MT6357,MT6358 */ #define RTC_BBPU_AUTO BIT(3) #define RTC_BBPU_CLR BIT(1) /* MT6359, MT6359p*/ #define RTC_BBPU_AL_STA BIT(7) #define RTC_BBPU_RESET_AL BIT(3) #define RTC_BBPU_RESET_SPAR BIT(2) #define RTC_AL_MASK_DOW BIT(4) #define RTC_IRQ_EN_LP BIT(3) #define RTC_IRQ_EN_ONESHOT BIT(2) #define RTC_IRQ_EN_AL BIT(0) #define RTC_IRQ_EN_ONESHOT_AL (RTC_IRQ_EN_ONESHOT | RTC_IRQ_EN_AL) #define RTC_IRQ_STA_LP BIT(3) #define RTC_IRQ_STA_AL BIT(0) #define RTC_PDN1_PWRON_TIME BIT(7) #define RTC_PDN2_PWRON_LOGO BIT(15) #define RTC_PDN2_PWRON_ALARM BIT(4) #define RTC_POFF_ALM_SET _IOW('p', 0x15, struct rtc_time) /* Set alarm time */ static u16 rtc_alarm_reg[RTC_OFFSET_COUNT][3] = { {RTC_AL_SEC, RTC_AL_SEC_MASK, 0}, {RTC_AL_MIN, RTC_AL_MIN_MASK, 0}, {RTC_AL_HOU, RTC_AL_HOU_MASK, 0}, {RTC_AL_DOM, RTC_AL_DOM_MASK, 0}, {RTC_AL_DOW, RTC_AL_DOW_MASK, 0}, {RTC_AL_MTH, RTC_AL_MTH_MASK, 0}, {RTC_AL_YEA, RTC_AL_YEA_MASK, 0}, }; static u16 rtc_pwron_reg[RTC_OFFSET_COUNT][3] = { {RTC_SPAR0, RTC_PWRON_SEC_MASK, RTC_PWRON_SEC_SHIFT}, {RTC_SPAR1, RTC_PWRON_MIN_MASK, RTC_PWRON_MIN_SHIFT}, {RTC_SPAR1, RTC_PWRON_HOU_MASK, RTC_PWRON_HOU_SHIFT}, {RTC_SPAR1, RTC_PWRON_DOM_MASK, RTC_PWRON_DOM_SHIFT}, {0, 0, 0}, {RTC_PDN2, RTC_PWRON_MTH_MASK, RTC_PWRON_MTH_SHIFT}, {RTC_PDN2, RTC_PWRON_YEA_MASK, RTC_PWRON_YEA_SHIFT}, }; enum rtc_reg_set { RTC_REG, RTC_MASK, RTC_SHIFT }; enum rtc_irq_sta { RTC_NONE, RTC_ALSTA, RTC_TCSTA, RTC_LPSTA, }; struct mt6358_rtc { struct device *dev; struct rtc_device *rtc_dev; spinlock_t lock; struct regmap *regmap; int irq; u32 addr_base; struct work_struct work; struct completion comp; }; struct tag_bootmode { u32 size; u32 tag; u32 bootmode; u32 boottype; }; static struct mt6358_rtc *mt_rtc; static struct wakeup_source *mt6358_rtc_suspend_lock; static int rtc_show_time; static int rtc_show_alarm = 1; static int apply_lpsd_solution; /*for KPOC alarm*/ static bool rtc_pm_notifier_registered; static bool kpoc_alarm; static unsigned long rtc_pm_status; static int alarm1m15s; module_param(rtc_show_time, int, 0644); module_param(rtc_show_alarm, int, 0644); void __attribute__((weak)) arch_reset(char mode, const char *cmd) { pr_info("arch_reset is not ready\n"); } static int rtc_read(unsigned int reg, unsigned int *val) { return regmap_read(mt_rtc->regmap, mt_rtc->addr_base + reg, val); } static int rtc_write(unsigned int reg, unsigned int val) { return regmap_write(mt_rtc->regmap, mt_rtc->addr_base + reg, val); } static int rtc_update_bits(unsigned int reg, unsigned int mask, unsigned int val) { return regmap_update_bits(mt_rtc->regmap, mt_rtc->addr_base + reg, mask, val); } static int rtc_field_read(unsigned int reg, unsigned int mask, unsigned int shift, unsigned int *val) { int ret; unsigned int reg_val = 0; ret = rtc_read(reg, ®_val); if (ret != 0) return ret; reg_val &= mask; reg_val >>= shift; *val = reg_val; return ret; } #define BULK_WRITE 0 #define BULK_READ 1 static int rtc_bulk_access(int mode, unsigned int reg, void *val, size_t val_count) { if (mode == BULK_WRITE) { return regmap_bulk_write(mt_rtc->regmap, mt_rtc->addr_base + reg, val, val_count); } else if (mode == BULK_READ) { return regmap_bulk_read(mt_rtc->regmap, mt_rtc->addr_base + reg, val, val_count); } else return -EPERM; } static int rtc_write_trigger(void) { int ret, bbpu = 0; unsigned long long timeout = sched_clock() + 500000000; u32 pwrkey1 = 0, pwrkey2 = 0, sec = 0; ret = rtc_write(RTC_WRTGR, 1); if (ret < 0) return ret; do { ret = rtc_read(RTC_BBPU, &bbpu); if (ret < 0) break; if ((bbpu & RTC_BBPU_CBUSY) == 0) break; else if (sched_clock() > timeout) { rtc_read(RTC_BBPU, &bbpu); rtc_read(RTC_POWERKEY1, &pwrkey1); rtc_read(RTC_POWERKEY2, &pwrkey2); rtc_read(RTC_TC_SEC, &sec); pr_err("%s, wait cbusy timeout, %x, %x, %x, %d\n", __func__, bbpu, pwrkey1, pwrkey2, sec); ret = -ETIMEDOUT; break; } } while (1); return ret; } static int mtk_rtc_read_time(struct rtc_time *tm) { u16 data[RTC_OFFSET_COUNT]; int ret; u32 sec = 0; unsigned long long timeout = sched_clock() + 500000000; do { ret = rtc_bulk_access(BULK_READ, RTC_TC_SEC, data, RTC_OFFSET_COUNT); if (ret < 0) goto exit; tm->tm_sec = data[RTC_OFFSET_SEC] & RTC_TC_SEC_MASK; tm->tm_min = data[RTC_OFFSET_MIN] & RTC_TC_MIN_MASK; tm->tm_hour = data[RTC_OFFSET_HOUR] & RTC_TC_HOU_MASK; tm->tm_mday = data[RTC_OFFSET_DOM] & RTC_TC_DOM_MASK; tm->tm_mon = data[RTC_OFFSET_MTH] & RTC_TC_MTH_MASK; tm->tm_year = data[RTC_OFFSET_YEAR] & RTC_TC_YEA_MASK; ret = rtc_read(RTC_TC_SEC, &sec); if (ret < 0) goto exit; if (sched_clock() > timeout) { pr_notice("%s, time out\n", __func__); break; } } while (sec < tm->tm_sec); return ret; exit: pr_err("%s error\n", __func__); return ret; } static int mtk_rtc_set_alarm(struct rtc_time *tm) { int ret, i; u16 data[RTC_OFFSET_COUNT]; data[RTC_OFFSET_SEC] = tm->tm_sec & RTC_AL_SEC_MASK; data[RTC_OFFSET_MIN] = tm->tm_min & RTC_AL_MIN_MASK; data[RTC_OFFSET_HOUR] = tm->tm_hour & RTC_AL_HOU_MASK; data[RTC_OFFSET_DOM] = tm->tm_mday & RTC_AL_DOM_MASK; data[RTC_OFFSET_MTH] = tm->tm_mon & RTC_AL_MTH_MASK; data[RTC_OFFSET_YEAR] = tm->tm_year & RTC_AL_YEA_MASK; for (i = RTC_OFFSET_SEC; i < RTC_OFFSET_COUNT; i++) { if (i == RTC_OFFSET_DOW) continue; ret = rtc_update_bits(rtc_alarm_reg[i][RTC_REG], rtc_alarm_reg[i][RTC_MASK], data[i]); if (ret < 0) goto exit; } ret = rtc_write(RTC_AL_MASK, RTC_AL_MASK_DOW); /* mask DOW */ if (ret < 0) goto exit; ret = rtc_write_trigger(); if (ret < 0) goto exit; ret = rtc_update_bits(RTC_IRQ_EN, RTC_IRQ_EN_ONESHOT_AL, RTC_IRQ_EN_ONESHOT_AL); if (ret < 0) goto exit; ret = rtc_write_trigger(); if (ret < 0) goto exit; return ret; exit: pr_err("%s error\n", __func__); return ret; } bool mtk_rtc_is_pwron_alarm(struct rtc_time *nowtm, struct rtc_time *tm) { u32 pdn1 = 0; u32 data[RTC_OFFSET_COUNT] = {0}; int ret, i; ret = rtc_read(RTC_PDN1, &pdn1); if (ret < 0) goto exit; pr_notice("pdn1 = 0x%4x\n", pdn1); if (pdn1 & RTC_PDN1_PWRON_TIME) { /* power-on time is available */ ret = mtk_rtc_read_time(nowtm); if (ret < 0) goto exit; for (i = RTC_OFFSET_SEC; i < RTC_OFFSET_COUNT; i++) { if (i == RTC_OFFSET_DOW) continue; ret = rtc_field_read(rtc_pwron_reg[i][RTC_REG], rtc_pwron_reg[i][RTC_MASK], rtc_pwron_reg[i][RTC_SHIFT], &data[i]); if (ret < 0) goto exit; } tm->tm_sec = data[RTC_OFFSET_SEC]; tm->tm_min = data[RTC_OFFSET_MIN]; tm->tm_hour = data[RTC_OFFSET_HOUR]; tm->tm_mday = data[RTC_OFFSET_DOM]; tm->tm_mon = data[RTC_OFFSET_MTH]; tm->tm_year = data[RTC_OFFSET_YEAR]; return true; } return false; exit: pr_err("%s error\n", __func__); return false; } static int mtk_rtc_set_pwron_alarm_time(struct rtc_time *tm) { u16 data[RTC_OFFSET_COUNT]; int ret, i; pr_err("%s\n", __func__); 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); for (i = RTC_OFFSET_SEC; i < RTC_OFFSET_COUNT; i++) { if (i == RTC_OFFSET_DOW) continue; ret = rtc_update_bits(rtc_pwron_reg[i][RTC_REG], rtc_pwron_reg[i][RTC_MASK], data[i]); if (ret < 0) goto exit; ret = rtc_write_trigger(); if (ret < 0) goto exit; } return ret; exit: pr_err("%s error\n", __func__); return ret; } static int mtk_rtc_set_pwron_alarm(bool enable, struct rtc_time *tm, bool logo) { u16 pdn1 = 0, pdn2 = 0; int ret; ret = mtk_rtc_set_pwron_alarm_time(tm); if (ret < 0) goto exit; if (enable) pdn1 = RTC_PDN1_PWRON_TIME; ret = rtc_update_bits(RTC_PDN1, RTC_PDN1_PWRON_TIME, pdn1); if (ret < 0) goto exit; if (logo) pdn2 = RTC_PDN2_PWRON_LOGO; ret = rtc_update_bits(RTC_PDN2, RTC_PDN2_PWRON_LOGO, pdn2); if (ret < 0) goto exit; ret = rtc_write_trigger(); if (ret < 0) goto exit; return ret; exit: pr_err("%s error\n", __func__); return ret; } void rtc_read_pwron_alarm(struct rtc_wkalrm *alm) { struct rtc_time *tm; u32 pdn1 = 0, pdn2 = 0; u16 data[RTC_OFFSET_COUNT]; unsigned long flags; int ret; if (alm == NULL) return; tm = &alm->time; spin_lock_irqsave(&mt_rtc->lock, flags); ret = rtc_read(RTC_PDN1, &pdn1); if (ret < 0) goto exit; ret = rtc_read(RTC_PDN2, &pdn2); if (ret < 0) goto exit; 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); ret = rtc_bulk_access(BULK_READ, RTC_AL_SEC, data, RTC_OFFSET_COUNT); if (ret < 0) goto exit; tm->tm_sec = data[RTC_OFFSET_SEC] & RTC_AL_SEC_MASK; tm->tm_min = data[RTC_OFFSET_MIN] & RTC_AL_MIN_MASK; tm->tm_hour = data[RTC_OFFSET_HOUR] & RTC_AL_HOU_MASK; tm->tm_mday = data[RTC_OFFSET_DOM] & RTC_AL_DOM_MASK; tm->tm_mon = data[RTC_OFFSET_MTH] & RTC_AL_MTH_MASK; tm->tm_year = data[RTC_OFFSET_YEAR] & RTC_AL_YEA_MASK; spin_unlock_irqrestore(&mt_rtc->lock, flags); tm->tm_year += RTC_MIN_YEAR_OFFSET; tm->tm_mon -= 1; if (rtc_show_alarm) { pr_notice("power-on = %04d/%02d/%02d %02d:%02d:%02d (%d)(%d)\n", tm->tm_year + RTC_BASE_YEAR, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, alm->enabled, alm->pending); } return; exit: pr_err("%s error\n", __func__); } #ifdef CONFIG_PM #define PM_DUMMY 0xFFFF static int rtc_pm_event(struct notifier_block *notifier, unsigned long pm_event, void *unused) { pr_notice("%s = %lu\n", __func__, pm_event); switch (pm_event) { case PM_SUSPEND_PREPARE: rtc_pm_status = PM_SUSPEND_PREPARE; return NOTIFY_DONE; case PM_POST_SUSPEND: rtc_pm_status = PM_POST_SUSPEND; break; default: rtc_pm_status = PM_DUMMY; break; } if (kpoc_alarm) { pr_notice("%s trigger reboot\n", __func__); complete(&mt_rtc->comp); kpoc_alarm = false; } return NOTIFY_DONE; } static struct notifier_block rtc_pm_notifier_func = { .notifier_call = rtc_pm_event, .priority = 0, }; #endif /* CONFIG_PM */ static void mtk_rtc_work_queue(struct work_struct *work) { struct mt6358_rtc *rtc = container_of(work, struct mt6358_rtc, work); unsigned long ret; unsigned int msecs; ret = wait_for_completion_timeout(&rtc->comp, msecs_to_jiffies(30000)); if (!ret) { pr_notice("%s timeout\n", __func__); BUG_ON(1); } else { msecs = jiffies_to_msecs(ret); pr_notice("%s timeleft= %d\n", __func__, msecs); rtc_mark_kpoc(); kernel_restart("kpoc"); } } static void mtk_rtc_reboot(void) { __pm_stay_awake(mt6358_rtc_suspend_lock); init_completion(&mt_rtc->comp); schedule_work_on(cpumask_first(cpu_online_mask), &mt_rtc->work); if (!rtc_pm_notifier_registered) goto reboot; if (rtc_pm_status != PM_SUSPEND_PREPARE) goto reboot; kpoc_alarm = true; pr_notice("%s:wait\n", __func__); return; reboot: pr_notice("%s:trigger\n", __func__); complete(&mt_rtc->comp); } #ifndef USER_BUILD_KERNEL void mtk_rtc_lp_exception(void) { u32 bbpu = 0, irqsta = 0, irqen = 0, osc32 = 0; u32 pwrkey1 = 0, pwrkey2 = 0, prot = 0, con = 0, sec1 = 0, sec2 = 0; rtc_read(RTC_BBPU, &bbpu); rtc_read(RTC_IRQ_STA, &irqsta); rtc_read(RTC_IRQ_EN, &irqen); rtc_read(RTC_OSC32CON, &osc32); rtc_read(RTC_POWERKEY1, &pwrkey1); rtc_read(RTC_POWERKEY2, &pwrkey2); rtc_read(RTC_PROT, &prot); rtc_read(RTC_CON, &con); rtc_read(RTC_TC_SEC, &sec1); msleep(2000); rtc_read(RTC_TC_SEC, &sec2); 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 static int mtk_rtc_is_alarm_irq(void) { u32 irqsta = 0, bbpu; int ret, val; ret = rtc_read(RTC_IRQ_STA, &irqsta); /* read clear */ if ((ret == 0) && (irqsta & RTC_IRQ_STA_AL)) { bbpu = RTC_BBPU_KEY | RTC_BBPU_PWREN; rtc_write(RTC_BBPU, bbpu); val = rtc_write_trigger(); if (val < 0) pr_notice("%s error\n", __func__); return RTC_ALSTA; } #ifndef USER_BUILD_KERNEL if ((ret == 0) && (irqsta & RTC_IRQ_STA_LP)) { mtk_rtc_lp_exception(); return RTC_LPSTA; } #endif return RTC_NONE; } static void mtk_rtc_update_pwron_alarm_flag(void) { int ret; ret = rtc_update_bits(RTC_PDN1, RTC_PDN1_PWRON_TIME, 0); if (ret < 0) goto exit; ret = rtc_update_bits(RTC_PDN2, RTC_PDN2_PWRON_ALARM, RTC_PDN2_PWRON_ALARM); if (ret < 0) goto exit; ret = rtc_write_trigger(); if (ret < 0) goto exit; return; exit: pr_err("%s error\n", __func__); } static void mtk_rtc_reset_bbpu_alarm_status(void) { u32 bbpu = RTC_BBPU_KEY | RTC_BBPU_PWREN; int ret; if (apply_lpsd_solution) { pr_notice("%s:lpsd\n", __func__); return; } #if defined(CONFIG_MTK_PMIC_CHIP_MT6358) || \ defined(CONFIG_MTK_PMIC_CHIP_MT6357) bbpu |= RTC_BBPU_CLR; #endif #if defined(CONFIG_MTK_PMIC_CHIP_MT6359) || \ defined(CONFIG_MTK_PMIC_CHIP_MT6359P) bbpu |= RTC_BBPU_RESET_AL; #endif rtc_write(RTC_BBPU, bbpu); ret = rtc_write_trigger(); if (ret < 0) pr_err("%s error\n", __func__); return; } static irqreturn_t mtk_rtc_irq_handler(int irq, void *data) { bool pwron_alm = false, pwron_alarm = false; struct rtc_time nowtm, tm; int status = RTC_NONE; unsigned long flags; // modify get_boot_mode struct device *dev = NULL; struct device_node *boot_node = NULL; struct tag_bootmode *tag = NULL; int boot_mode = 11;//UNKNOWN_BOOT dev = mt_rtc->dev; if (dev != NULL){ boot_node = of_parse_phandle(dev->of_node, "bootmode", 0); if (!boot_node){ pr_notice("%s: failed to get boot mode phandle\n", __func__); } else { tag = (struct tag_bootmode *)of_get_property(boot_node, "atag,boot", NULL); if (!tag){ pr_notice("%s: failed to get atag,boot\n", __func__); } else boot_mode = tag->bootmode; } } spin_lock_irqsave(&mt_rtc->lock, flags); status = mtk_rtc_is_alarm_irq(); pr_notice("%s:%d\n", __func__, status); if (status == RTC_NONE) { spin_unlock_irqrestore(&mt_rtc->lock, flags); return IRQ_NONE; } if (status == RTC_LPSTA) { spin_unlock_irqrestore(&mt_rtc->lock, flags); return IRQ_HANDLED; } mtk_rtc_reset_bbpu_alarm_status(); pwron_alarm = mtk_rtc_is_pwron_alarm(&nowtm, &tm); nowtm.tm_year += RTC_MIN_YEAR; tm.tm_year += RTC_MIN_YEAR; if (pwron_alarm) { time64_t now_time, time; now_time = mktime(nowtm.tm_year, nowtm.tm_mon, nowtm.tm_mday, nowtm.tm_hour, nowtm.tm_min, nowtm.tm_sec); if (now_time == -1) { spin_unlock_irqrestore(&mt_rtc->lock, flags); goto out; } time = mktime(tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); if (time == -1) { spin_unlock_irqrestore(&mt_rtc->lock, flags); goto out; } /* power on */ if (now_time >= time - 1 && now_time <= time + 4) { if (boot_mode == KERNEL_POWER_OFF_CHARGING_BOOT || boot_mode == LOW_POWER_OFF_CHARGING_BOOT) { mtk_rtc_reboot(); spin_unlock_irqrestore(&mt_rtc->lock, flags); disable_irq_nosync(mt_rtc->irq); goto out; } else { mtk_rtc_update_pwron_alarm_flag(); pwron_alm = true; } } else if (now_time < time) { /* set power-on alarm */ time -= 1; rtc_time64_to_tm(time, &tm); tm.tm_year -= RTC_MIN_YEAR_OFFSET; tm.tm_mon += 1; mtk_rtc_set_alarm(&tm); } } spin_unlock_irqrestore(&mt_rtc->lock, flags); out: if (mt_rtc->rtc_dev != NULL) rtc_update_irq(mt_rtc->rtc_dev, 1, RTC_IRQF | RTC_AF); if (rtc_show_alarm) pr_notice("%s time is up\n", pwron_alm ? "power-on" : "alarm"); return IRQ_HANDLED; } static int rtc_ops_read_time(struct device *dev, struct rtc_time *tm) { unsigned long long time; unsigned long flags; struct mt6358_rtc *rtc = dev_get_drvdata(dev); int ret; spin_lock_irqsave(&rtc->lock, flags); ret = mtk_rtc_read_time(tm); if (ret < 0) goto exit; spin_unlock_irqrestore(&rtc->lock, flags); tm->tm_year += RTC_MIN_YEAR_OFFSET; tm->tm_mon--; time = rtc_tm_to_time64(tm); do_div(time, 86400); time += 4; tm->tm_wday = do_div(time, 7); /* 1970/01/01 is Thursday */ if (rtc_show_time) { pr_notice("read tc time = %04d/%02d/%02d (%d) %02d:%02d:%02d\n", tm->tm_year + RTC_BASE_YEAR, tm->tm_mon + 1, tm->tm_mday, tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec); } return ret; exit: spin_unlock_irqrestore(&rtc->lock, flags); pr_err("%s error\n", __func__); return ret; } static int rtc_ops_set_time(struct device *dev, struct rtc_time *tm) { struct mt6358_rtc *rtc = dev_get_drvdata(dev); unsigned long flags; u16 data[RTC_OFFSET_COUNT]; int ret; if (tm->tm_year > 195) { pr_err("%s: invalid year %04d > 2095\n", __func__, tm->tm_year + RTC_BASE_YEAR); return -EINVAL; } pr_notice("set tc time = %04d/%02d/%02d %02d:%02d:%02d\n", tm->tm_year + RTC_BASE_YEAR, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); tm->tm_year -= RTC_MIN_YEAR_OFFSET; tm->tm_mon++; data[RTC_OFFSET_SEC] = tm->tm_sec; data[RTC_OFFSET_MIN] = tm->tm_min; data[RTC_OFFSET_HOUR] = tm->tm_hour; data[RTC_OFFSET_DOM] = tm->tm_mday; data[RTC_OFFSET_MTH] = tm->tm_mon; data[RTC_OFFSET_YEAR] = tm->tm_year; spin_lock_irqsave(&rtc->lock, flags); ret = rtc_bulk_access(BULK_WRITE, RTC_TC_SEC, data, RTC_OFFSET_COUNT); if (ret < 0) goto exit; ret = rtc_write_trigger(); if (ret < 0) goto exit; spin_unlock_irqrestore(&rtc->lock, flags); return ret; exit: spin_unlock_irqrestore(&rtc->lock, flags); pr_err("%s error\n", __func__); return ret; } static int rtc_ops_read_alarm(struct device *dev, struct rtc_wkalrm *alm) { unsigned long flags; struct rtc_time *tm = &alm->time; struct mt6358_rtc *rtc = dev_get_drvdata(dev); u32 irqen = 0, pdn2 = 0; u16 data[RTC_OFFSET_COUNT]; int ret; spin_lock_irqsave(&rtc->lock, flags); ret = rtc_read(RTC_IRQ_EN, &irqen); if (ret < 0) goto exit; alm->enabled = !!(irqen & RTC_IRQ_EN_AL); /* return Power-On Alarm bit */ ret = rtc_read(RTC_PDN2, &pdn2); if (ret < 0) goto exit; alm->pending = !!(pdn2 & RTC_PDN2_PWRON_ALARM); ret = rtc_bulk_access(BULK_READ, RTC_AL_SEC, data, RTC_OFFSET_COUNT); if (ret < 0) goto exit; tm->tm_sec = data[RTC_OFFSET_SEC] & RTC_AL_SEC_MASK; tm->tm_min = data[RTC_OFFSET_MIN] & RTC_AL_MIN_MASK; tm->tm_hour = data[RTC_OFFSET_HOUR] & RTC_AL_HOU_MASK; tm->tm_mday = data[RTC_OFFSET_DOM] & RTC_AL_DOM_MASK; tm->tm_mon = data[RTC_OFFSET_MTH] & RTC_AL_MTH_MASK; tm->tm_year = data[RTC_OFFSET_YEAR] & RTC_AL_YEA_MASK; spin_unlock_irqrestore(&rtc->lock, flags); tm->tm_year += RTC_MIN_YEAR_OFFSET; tm->tm_mon--; pr_notice("read al time = %04d/%02d/%02d %02d:%02d:%02d (%d)\n", tm->tm_year + RTC_BASE_YEAR, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, alm->enabled); return ret; exit: spin_unlock_irqrestore(&rtc->lock, flags); pr_err("%s error\n", __func__); return ret; } static int rtc_ops_set_alarm(struct device *dev, struct rtc_wkalrm *alm) { unsigned long flags; struct rtc_time tm = alm->time; ktime_t target; struct mt6358_rtc *rtc = dev_get_drvdata(dev); u32 irqsta; int ret = 0; if (tm.tm_year > 195) { pr_err("%s: invalid year %04d > 2095\n", __func__, tm.tm_year + RTC_BASE_YEAR); return -EINVAL; } 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); } else if (alm->enabled == 5) { /* Power on system 1 minute earlier */ alarm1m15s = 1; } tm.tm_year -= RTC_MIN_YEAR_OFFSET; tm.tm_mon++; pr_notice("set al time = %04d/%02d/%02d %02d:%02d:%02d (%d)\n", tm.tm_year + RTC_MIN_YEAR, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, alm->enabled); spin_lock_irqsave(&rtc->lock, flags); if (alm->enabled == 2) { /* enable power-on alarm */ ret = mtk_rtc_set_pwron_alarm(true, &tm, false); } else if (alm->enabled == 3 || alm->enabled == 5) { /* enable power-on alarm with logo */ ret = mtk_rtc_set_pwron_alarm(true, &tm, true); } else if (alm->enabled == 4) { /* disable power-on alarm */ ret = mtk_rtc_set_pwron_alarm(false, &tm, false); alarm1m15s = 0; } if (ret < 0) goto exit; /* disable alarm and clear Power-On Alarm bit */ ret = rtc_update_bits(RTC_IRQ_EN, RTC_IRQ_EN_AL, 0); if (ret < 0) goto exit; ret = rtc_update_bits(RTC_PDN2, RTC_PDN2_PWRON_ALARM, 0); if (ret < 0) goto exit; ret = rtc_write_trigger(); if (ret < 0) goto exit; ret = rtc_read(RTC_IRQ_STA, &irqsta); /* read clear */ if (ret < 0) goto exit; if (alm->enabled) ret = mtk_rtc_set_alarm(&tm); spin_unlock_irqrestore(&rtc->lock, flags); return ret; exit: spin_unlock_irqrestore(&rtc->lock, flags); pr_err("%s error\n", __func__); return ret; } int mtk_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 = rtc_ops_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; rtc_ops_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 = mtk_set_power_on(dev, &alm); break; default: err = -EINVAL; break; } return err; } static const struct rtc_class_ops rtc_ops = { .ioctl = mtk_rtc_ioctl, .read_time = rtc_ops_read_time, .set_time = rtc_ops_set_time, .read_alarm = rtc_ops_read_alarm, .set_alarm = rtc_ops_set_alarm, }; static void mtk_rtc_set_lp_irq(void) { unsigned int irqen = 0; int ret; #ifndef USER_BUILD_KERNEL irqen = RTC_IRQ_EN_LP; #endif ret = rtc_update_bits(RTC_IRQ_EN, RTC_IRQ_EN_LP, irqen); if (ret < 0) goto exit; ret = rtc_write_trigger(); if (ret < 0) goto exit; return; exit: pr_err("%s error\n", __func__); } static int mtk_rtc_pdrv_probe(struct platform_device *pdev) { #ifndef IPIMB struct mt6358_chip *mt6358_chip = dev_get_drvdata(pdev->dev.parent); #endif struct mt6358_rtc *rtc; unsigned long flags; int ret; #if IS_ENABLED(CONFIG_MTK_RTC) struct platform_device *plt_dev; #endif rtc = devm_kzalloc(&pdev->dev, sizeof(struct mt6358_rtc), GFP_KERNEL); if (!rtc) return -ENOMEM; rtc->irq = platform_get_irq(pdev, 0); if (rtc->irq <= 0) return -EINVAL; pr_notice("%s: rtc->irq = %d(%d)\n", __func__, rtc->irq, platform_get_irq_byname(pdev, "rtc")); #ifndef IPIMB rtc->regmap = mt6358_chip->regmap; #else rtc->regmap = dev_get_regmap(pdev->dev.parent->parent, NULL); #endif if (!rtc->regmap) { pr_err("%s: get regmap failed\n", __func__); return -ENODEV; } rtc->dev = &pdev->dev; spin_lock_init(&rtc->lock); mt_rtc = rtc; platform_set_drvdata(pdev, rtc); if (of_property_read_u32(pdev->dev.of_node, "base", &rtc->addr_base)) rtc->addr_base = RTC_DSN_ID; pr_notice("%s: rtc->addr_base =0x%x\n", __func__, rtc->addr_base); spin_lock_irqsave(&rtc->lock, flags); mtk_rtc_set_lp_irq(); spin_unlock_irqrestore(&rtc->lock, flags); mt6358_rtc_suspend_lock = wakeup_source_register(NULL, "mt6358-rtc suspend wakelock"); #ifdef CONFIG_PM if (register_pm_notifier(&rtc_pm_notifier_func)) pr_notice("rtc pm failed\n"); else rtc_pm_notifier_registered = true; #endif /* CONFIG_PM */ INIT_WORK(&rtc->work, mtk_rtc_work_queue); ret = request_threaded_irq(rtc->irq, NULL, mtk_rtc_irq_handler, IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mt6358-rtc", rtc); if (ret) { dev_dbg(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", rtc->irq, ret); goto out_dispose_irq; } device_init_wakeup(&pdev->dev, 1); /* register rtc device (/dev/rtc0) */ rtc->rtc_dev = rtc_device_register(RTC_NAME, &pdev->dev, &rtc_ops, THIS_MODULE); if (IS_ERR(rtc->rtc_dev)) { dev_dbg(&pdev->dev, "register rtc device failed\n"); ret = PTR_ERR(rtc->rtc_dev); goto out_free_irq; } if (of_property_read_bool(pdev->dev.of_node, "apply-lpsd-solution")) { apply_lpsd_solution = 1; pr_notice("%s: apply_lpsd_solution\n", __func__); } #if IS_ENABLED(CONFIG_MTK_RTC) plt_dev = platform_device_register_data(&pdev->dev, "mtk_rtc_dbg", -1, NULL, 0); if (IS_ERR(plt_dev)) dev_notice(&pdev->dev, "%s: failed to register mtk_rtc_dbg\n", __func__); #endif return 0; out_free_irq: free_irq(rtc->irq, rtc->rtc_dev); out_dispose_irq: irq_dispose_mapping(rtc->irq); return ret; } static int mtk_rtc_pdrv_remove(struct platform_device *pdev) { struct mt6358_rtc *rtc = platform_get_drvdata(pdev); rtc_device_unregister(rtc->rtc_dev); return 0; } static void mtk_rtc_pdrv_shutdown(struct platform_device *pdev) { struct rtc_time rtc_time_now; struct rtc_time rtc_time_alarm; ktime_t ktime_now; ktime_t ktime_alarm; bool is_pwron_alarm; if (alarm1m15s == 1) { is_pwron_alarm = mtk_rtc_is_pwron_alarm(&rtc_time_now, &rtc_time_alarm); if (is_pwron_alarm) { rtc_time_now.tm_year += RTC_MIN_YEAR_OFFSET; rtc_time_now.tm_mon--; rtc_time_alarm.tm_year += RTC_MIN_YEAR_OFFSET; rtc_time_alarm.tm_mon--; pr_notice("now = %04d/%02d/%02d %02d:%02d:%02d\n", rtc_time_now.tm_year + 1900, rtc_time_now.tm_mon + 1, rtc_time_now.tm_mday, rtc_time_now.tm_hour, rtc_time_now.tm_min, rtc_time_now.tm_sec); pr_notice("alarm = %04d/%02d/%02d %02d:%02d:%02d\n", rtc_time_alarm.tm_year + 1900, rtc_time_alarm.tm_mon + 1, rtc_time_alarm.tm_mday, rtc_time_alarm.tm_hour, rtc_time_alarm.tm_min, rtc_time_alarm.tm_sec); ktime_now = rtc_tm_to_ktime(rtc_time_now); ktime_alarm = rtc_tm_to_ktime(rtc_time_alarm); if (ktime_after(ktime_alarm, ktime_now)) { /* alarm has not happened */ ktime_alarm = ktime_sub_ms(ktime_alarm, MSEC_PER_SEC * 60); if (ktime_after(ktime_alarm, ktime_now)) pr_notice("Alarm will happen after 1 minute\n"); else { ktime_alarm = ktime_add_ms(ktime_now, MSEC_PER_SEC * 15); pr_notice("Alarm will happen in 15 seconds\n"); } rtc_time_alarm = rtc_ktime_to_tm(ktime_alarm); pr_notice("new alarm = %04d/%02d/%02d %02d:%02d:%02d\n", rtc_time_alarm.tm_year + 1900, rtc_time_alarm.tm_mon + 1, rtc_time_alarm.tm_mday, rtc_time_alarm.tm_hour, rtc_time_alarm.tm_min, rtc_time_alarm.tm_sec); rtc_time_alarm.tm_year -= RTC_MIN_YEAR_OFFSET; rtc_time_alarm.tm_mon++; mtk_rtc_set_pwron_alarm_time(&rtc_time_alarm); mtk_rtc_set_alarm(&rtc_time_alarm); } else pr_notice("Alarm has happened before\n"); } else pr_notice("No power-off alarm is set\n"); } } static const struct of_device_id mt6358_rtc_of_match[] = { { .compatible = "mediatek,mt6357-rtc", }, { .compatible = "mediatek,mt6358-rtc", }, { .compatible = "mediatek,mt6359-rtc", }, { } }; MODULE_DEVICE_TABLE(of, mt6358_rtc_of_match); static struct platform_driver mtk_rtc_pdrv = { .probe = mtk_rtc_pdrv_probe, .remove = mtk_rtc_pdrv_remove, .shutdown = mtk_rtc_pdrv_shutdown, .driver = { .name = RTC_NAME, .owner = THIS_MODULE, .of_match_table = mt6358_rtc_of_match, }, }; module_platform_driver(mtk_rtc_pdrv); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Wilma Wu "); MODULE_DESCRIPTION("RTC Driver for MediaTek MT6358 PMIC");