[Feature] add poweralarm /wakup-alarm lib

Change-Id: Iff1697a5347d309e6d3ccbbcbf49f997abda76e4
(cherry picked from commit fbb60882f87a08ee6b21eba6ff10ed1898e25d04)
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,