summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/rtc/rtc-sun6i.c57
1 files changed, 40 insertions, 17 deletions
diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
index e1245ee4d99b..448e0e14a9ff 100644
--- a/drivers/rtc/rtc-sun6i.c
+++ b/drivers/rtc/rtc-sun6i.c
@@ -48,7 +48,8 @@
/* Alarm 0 (counter) */
#define SUN6I_ALRM_COUNTER 0x0020
-#define SUN6I_ALRM_CUR_VAL 0x0024
+/* This holds the remaining alarm seconds on older SoCs (current value) */
+#define SUN6I_ALRM_COUNTER_HMS 0x0024
#define SUN6I_ALRM_EN 0x0028
#define SUN6I_ALRM_EN_CNT_EN BIT(0)
#define SUN6I_ALRM_IRQ_EN 0x002c
@@ -523,32 +524,54 @@ static int sun6i_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
struct rtc_time *alrm_tm = &wkalrm->time;
struct rtc_time tm_now;
- time64_t time_now, time_set;
+ time64_t time_set;
+ u32 counter_val, counter_val_hms;
int ret;
- ret = sun6i_rtc_gettime(dev, &tm_now);
- if (ret < 0) {
- dev_err(dev, "Error in getting time\n");
- return -EINVAL;
- }
-
time_set = rtc_tm_to_time64(alrm_tm);
- time_now = rtc_tm_to_time64(&tm_now);
- if (time_set <= time_now) {
- dev_err(dev, "Date to set in the past\n");
- return -EINVAL;
- }
- if ((time_set - time_now) > U32_MAX) {
- dev_err(dev, "Date too far in the future\n");
- return -EINVAL;
+ if (chip->flags & RTC_LINEAR_DAY) {
+ /*
+ * The alarm registers hold the actual alarm time, encoded
+ * in the same way (linear day + HMS) as the current time.
+ */
+ counter_val_hms = SUN6I_TIME_SET_SEC_VALUE(alrm_tm->tm_sec) |
+ SUN6I_TIME_SET_MIN_VALUE(alrm_tm->tm_min) |
+ SUN6I_TIME_SET_HOUR_VALUE(alrm_tm->tm_hour);
+ /* The division will cut off the H:M:S part of alrm_tm. */
+ counter_val = div_u64(rtc_tm_to_time64(alrm_tm), SECS_PER_DAY);
+ } else {
+ /* The alarm register holds the number of seconds left. */
+ time64_t time_now;
+
+ ret = sun6i_rtc_gettime(dev, &tm_now);
+ if (ret < 0) {
+ dev_err(dev, "Error in getting time\n");
+ return -EINVAL;
+ }
+
+ time_now = rtc_tm_to_time64(&tm_now);
+ if (time_set <= time_now) {
+ dev_err(dev, "Date to set in the past\n");
+ return -EINVAL;
+ }
+ if ((time_set - time_now) > U32_MAX) {
+ dev_err(dev, "Date too far in the future\n");
+ return -EINVAL;
+ }
+
+ counter_val = time_set - time_now;
}
sun6i_rtc_setaie(0, chip);
writel(0, chip->base + SUN6I_ALRM_COUNTER);
+ if (chip->flags & RTC_LINEAR_DAY)
+ writel(0, chip->base + SUN6I_ALRM_COUNTER_HMS);
usleep_range(100, 300);
- writel(time_set - time_now, chip->base + SUN6I_ALRM_COUNTER);
+ writel(counter_val, chip->base + SUN6I_ALRM_COUNTER);
+ if (chip->flags & RTC_LINEAR_DAY)
+ writel(counter_val_hms, chip->base + SUN6I_ALRM_COUNTER_HMS);
chip->alarm = time_set;
sun6i_rtc_setaie(wkalrm->enabled, chip);