]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/rtc/rtc-cmos.c
Merge tag 'rtc-4.10' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux
[karo-tx-linux.git] / drivers / rtc / rtc-cmos.c
index 38aa8e1906c262dc9f367d94230422fb5a2be82c..f4a96dbdabf21ec4bdf8017e524ea42b9b0ce5c7 100644 (file)
@@ -332,14 +332,86 @@ static void cmos_irq_disable(struct cmos_rtc *cmos, unsigned char mask)
        cmos_checkintr(cmos, rtc_control);
 }
 
+static int cmos_validate_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+       struct cmos_rtc *cmos = dev_get_drvdata(dev);
+       struct rtc_time now;
+
+       cmos_read_time(dev, &now);
+
+       if (!cmos->day_alrm) {
+               time64_t t_max_date;
+               time64_t t_alrm;
+
+               t_max_date = rtc_tm_to_time64(&now);
+               t_max_date += 24 * 60 * 60 - 1;
+               t_alrm = rtc_tm_to_time64(&t->time);
+               if (t_alrm > t_max_date) {
+                       dev_err(dev,
+                               "Alarms can be up to one day in the future\n");
+                       return -EINVAL;
+               }
+       } else if (!cmos->mon_alrm) {
+               struct rtc_time max_date = now;
+               time64_t t_max_date;
+               time64_t t_alrm;
+               int max_mday;
+
+               if (max_date.tm_mon == 11) {
+                       max_date.tm_mon = 0;
+                       max_date.tm_year += 1;
+               } else {
+                       max_date.tm_mon += 1;
+               }
+               max_mday = rtc_month_days(max_date.tm_mon, max_date.tm_year);
+               if (max_date.tm_mday > max_mday)
+                       max_date.tm_mday = max_mday;
+
+               t_max_date = rtc_tm_to_time64(&max_date);
+               t_max_date -= 1;
+               t_alrm = rtc_tm_to_time64(&t->time);
+               if (t_alrm > t_max_date) {
+                       dev_err(dev,
+                               "Alarms can be up to one month in the future\n");
+                       return -EINVAL;
+               }
+       } else {
+               struct rtc_time max_date = now;
+               time64_t t_max_date;
+               time64_t t_alrm;
+               int max_mday;
+
+               max_date.tm_year += 1;
+               max_mday = rtc_month_days(max_date.tm_mon, max_date.tm_year);
+               if (max_date.tm_mday > max_mday)
+                       max_date.tm_mday = max_mday;
+
+               t_max_date = rtc_tm_to_time64(&max_date);
+               t_max_date -= 1;
+               t_alrm = rtc_tm_to_time64(&t->time);
+               if (t_alrm > t_max_date) {
+                       dev_err(dev,
+                               "Alarms can be up to one year in the future\n");
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
 static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
 {
        struct cmos_rtc *cmos = dev_get_drvdata(dev);
        unsigned char mon, mday, hrs, min, sec, rtc_control;
+       int ret;
 
        if (!is_valid_irq(cmos->irq))
                return -EIO;
 
+       ret = cmos_validate_alarm(dev, t);
+       if (ret < 0)
+               return ret;
+
        mon = t->time.tm_mon + 1;
        mday = t->time.tm_mday;
        hrs = t->time.tm_hour;
@@ -707,9 +779,6 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
 
        spin_unlock_irq(&rtc_lock);
 
-       /* FIXME:
-        * <asm-generic/rtc.h> doesn't know 12-hour mode either.
-        */
        if (is_valid_irq(rtc_irq) && !(rtc_control & RTC_24H)) {
                dev_warn(dev, "only 24-hr supported\n");
                retval = -ENXIO;