[Feature] add poweralarm /wakup-alarm lib
Change-Id: Iff1697a5347d309e6d3ccbbcbf49f997abda76e4
(cherry picked from commit fbb60882f87a08ee6b21eba6ff10ed1898e25d04)
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,